1 /* //device/content/providers/telephony/TelephonyProvider.java 2 ** 3 ** Copyright 2006, The Android Open Source Project 4 ** 5 ** Licensed under the Apache License, Version 2.0 (the "License"); 6 ** you may not use this file except in compliance with the License. 7 ** You may obtain a copy of the License at 8 ** 9 ** http://www.apache.org/licenses/LICENSE-2.0 10 ** 11 ** Unless required by applicable law or agreed to in writing, software 12 ** distributed under the License is distributed on an "AS IS" BASIS, 13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 ** See the License for the specific language governing permissions and 15 ** limitations under the License. 16 */ 17 18 package com.android.providers.telephony; 19 20 import android.content.ContentProvider; 21 import android.content.ContentUris; 22 import android.content.ContentValues; 23 import android.content.Context; 24 import android.content.SharedPreferences; 25 import android.content.UriMatcher; 26 import android.content.pm.PackageManager; 27 import android.content.res.Resources; 28 import android.content.res.XmlResourceParser; 29 import android.database.Cursor; 30 import android.database.SQLException; 31 import android.database.sqlite.SQLiteDatabase; 32 import android.database.sqlite.SQLiteException; 33 import android.database.sqlite.SQLiteOpenHelper; 34 import android.database.sqlite.SQLiteQueryBuilder; 35 import android.net.Uri; 36 import android.os.Binder; 37 import android.os.Environment; 38 import android.os.FileUtils; 39 import android.os.SystemProperties; 40 import android.os.UserHandle; 41 import android.telephony.ServiceState; 42 import android.telephony.SubscriptionInfo; 43 import android.telephony.SubscriptionManager; 44 import android.telephony.TelephonyManager; 45 import android.text.TextUtils; 46 import android.util.Log; 47 import android.util.Xml; 48 49 import com.android.internal.util.XmlUtils; 50 51 import org.xmlpull.v1.XmlPullParser; 52 import org.xmlpull.v1.XmlPullParserException; 53 54 import java.io.File; 55 import java.io.FileNotFoundException; 56 import java.io.FileReader; 57 import java.io.IOException; 58 import java.util.ArrayList; 59 import java.util.Arrays; 60 import java.util.List; 61 import java.util.Map; 62 63 import static android.provider.Telephony.Carriers.*; 64 65 public class TelephonyProvider extends ContentProvider 66 { 67 private static final String DATABASE_NAME = "telephony.db"; 68 private static final boolean DBG = true; 69 private static final boolean VDBG = false; // STOPSHIP if true 70 71 private static final int DATABASE_VERSION = 18 << 16; 72 private static final int URL_UNKNOWN = 0; 73 private static final int URL_TELEPHONY = 1; 74 private static final int URL_CURRENT = 2; 75 private static final int URL_ID = 3; 76 private static final int URL_RESTOREAPN = 4; 77 private static final int URL_PREFERAPN = 5; 78 private static final int URL_PREFERAPN_NO_UPDATE = 6; 79 private static final int URL_SIMINFO = 7; 80 private static final int URL_TELEPHONY_USING_SUBID = 8; 81 private static final int URL_CURRENT_USING_SUBID = 9; 82 private static final int URL_RESTOREAPN_USING_SUBID = 10; 83 private static final int URL_PREFERAPN_USING_SUBID = 11; 84 private static final int URL_PREFERAPN_NO_UPDATE_USING_SUBID = 12; 85 private static final int URL_SIMINFO_USING_SUBID = 13; 86 private static final int URL_UPDATE_DB = 14; 87 88 private static final String TAG = "TelephonyProvider"; 89 private static final String CARRIERS_TABLE = "carriers"; 90 private static final String CARRIERS_TABLE_TMP = "carriers_tmp"; 91 private static final String SIMINFO_TABLE = "siminfo"; 92 93 private static final String PREF_FILE_APN = "preferred-apn"; 94 private static final String COLUMN_APN_ID = "apn_id"; 95 96 private static final String PREF_FILE_FULL_APN = "preferred-full-apn"; 97 private static final String DB_VERSION_KEY = "version"; 98 99 private static final String BUILD_ID_FILE = "build-id"; 100 private static final String RO_BUILD_ID = "ro_build_id"; 101 102 private static final String PREF_FILE = "telephonyprovider"; 103 private static final String APN_CONF_CHECKSUM = "apn_conf_checksum"; 104 105 private static final String PARTNER_APNS_PATH = "etc/apns-conf.xml"; 106 private static final String OEM_APNS_PATH = "telephony/apns-conf.xml"; 107 private static final String OTA_UPDATED_APNS_PATH = "misc/apns-conf.xml"; 108 private static final String OLD_APNS_PATH = "etc/old-apns-conf.xml"; 109 110 private static final UriMatcher s_urlMatcher = new UriMatcher(UriMatcher.NO_MATCH); 111 112 private static final ContentValues s_currentNullMap; 113 private static final ContentValues s_currentSetMap; 114 115 private static final String IS_UNEDITED = EDITED + "=" + UNEDITED; 116 private static final String IS_EDITED = EDITED + "!=" + UNEDITED; 117 private static final String IS_USER_EDITED = EDITED + "=" + USER_EDITED; 118 private static final String IS_USER_DELETED = EDITED + "=" + USER_DELETED; 119 private static final String IS_NOT_USER_DELETED = EDITED + "!=" + USER_DELETED; 120 private static final String IS_USER_DELETED_BUT_PRESENT_IN_XML = 121 EDITED + "=" + USER_DELETED_BUT_PRESENT_IN_XML; 122 private static final String IS_NOT_USER_DELETED_BUT_PRESENT_IN_XML = 123 EDITED + "!=" + USER_DELETED_BUT_PRESENT_IN_XML; 124 private static final String IS_CARRIER_EDITED = EDITED + "=" + CARRIER_EDITED; 125 private static final String IS_CARRIER_DELETED = EDITED + "=" + CARRIER_DELETED; 126 private static final String IS_NOT_CARRIER_DELETED = EDITED + "!=" + CARRIER_DELETED; 127 private static final String IS_CARRIER_DELETED_BUT_PRESENT_IN_XML = 128 EDITED + "=" + CARRIER_DELETED_BUT_PRESENT_IN_XML; 129 private static final String IS_NOT_CARRIER_DELETED_BUT_PRESENT_IN_XML = 130 EDITED + "!=" + CARRIER_DELETED_BUT_PRESENT_IN_XML; 131 132 private static final int INVALID_APN_ID = -1; 133 private static final List<String> CARRIERS_UNIQUE_FIELDS = new ArrayList<String>(); 134 135 static { 136 // Columns not included in UNIQUE constraint: name, current, edited, user, server, password, 137 // authtype, type, protocol, roaming_protocol, sub_id, modem_cognitive, max_conns, 138 // wait_time, max_conns_time, mtu, bearer_bitmask, user_visible 139 CARRIERS_UNIQUE_FIELDS.add(NUMERIC); 140 CARRIERS_UNIQUE_FIELDS.add(MCC); 141 CARRIERS_UNIQUE_FIELDS.add(MNC); 142 CARRIERS_UNIQUE_FIELDS.add(APN); 143 CARRIERS_UNIQUE_FIELDS.add(PROXY); 144 CARRIERS_UNIQUE_FIELDS.add(PORT); 145 CARRIERS_UNIQUE_FIELDS.add(MMSPROXY); 146 CARRIERS_UNIQUE_FIELDS.add(MMSPORT); 147 CARRIERS_UNIQUE_FIELDS.add(MMSC); 148 CARRIERS_UNIQUE_FIELDS.add(CARRIER_ENABLED); 149 CARRIERS_UNIQUE_FIELDS.add(BEARER); 150 CARRIERS_UNIQUE_FIELDS.add(MVNO_TYPE); 151 CARRIERS_UNIQUE_FIELDS.add(MVNO_MATCH_DATA); 152 CARRIERS_UNIQUE_FIELDS.add(PROFILE_ID); 153 } 154 155 static { 156 s_urlMatcher.addURI("telephony", "carriers", URL_TELEPHONY); 157 s_urlMatcher.addURI("telephony", "carriers/current", URL_CURRENT); 158 s_urlMatcher.addURI("telephony", "carriers/#", URL_ID); 159 s_urlMatcher.addURI("telephony", "carriers/restore", URL_RESTOREAPN); 160 s_urlMatcher.addURI("telephony", "carriers/preferapn", URL_PREFERAPN); 161 s_urlMatcher.addURI("telephony", "carriers/preferapn_no_update", URL_PREFERAPN_NO_UPDATE); 162 163 s_urlMatcher.addURI("telephony", "siminfo", URL_SIMINFO); 164 165 s_urlMatcher.addURI("telephony", "carriers/subId/*", URL_TELEPHONY_USING_SUBID); 166 s_urlMatcher.addURI("telephony", "carriers/current/subId/*", URL_CURRENT_USING_SUBID); 167 s_urlMatcher.addURI("telephony", "carriers/restore/subId/*", URL_RESTOREAPN_USING_SUBID); 168 s_urlMatcher.addURI("telephony", "carriers/preferapn/subId/*", URL_PREFERAPN_USING_SUBID); 169 s_urlMatcher.addURI("telephony", "carriers/preferapn_no_update/subId/*", 170 URL_PREFERAPN_NO_UPDATE_USING_SUBID); 171 172 s_urlMatcher.addURI("telephony", "carriers/update_db", URL_UPDATE_DB); 173 174 s_currentNullMap = new ContentValues(1); s_currentNullMap.put(CURRENT, "0")175 s_currentNullMap.put(CURRENT, "0"); 176 177 s_currentSetMap = new ContentValues(1); s_currentSetMap.put(CURRENT, "1")178 s_currentSetMap.put(CURRENT, "1"); 179 } 180 181 private static class DatabaseHelper extends SQLiteOpenHelper { 182 // Context to access resources with 183 private Context mContext; 184 185 /** 186 * DatabaseHelper helper class for loading apns into a database. 187 * 188 * @param context of the user. 189 */ DatabaseHelper(Context context)190 public DatabaseHelper(Context context) { 191 super(context, DATABASE_NAME, null, getVersion(context)); 192 mContext = context; 193 } 194 getVersion(Context context)195 private static int getVersion(Context context) { 196 if (VDBG) log("getVersion:+"); 197 // Get the database version, combining a static schema version and the XML version 198 Resources r = context.getResources(); 199 XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns); 200 try { 201 XmlUtils.beginDocument(parser, "apns"); 202 int publicversion = Integer.parseInt(parser.getAttributeValue(null, "version")); 203 int version = DATABASE_VERSION | publicversion; 204 if (VDBG) log("getVersion:- version=0x" + Integer.toHexString(version)); 205 return version; 206 } catch (Exception e) { 207 loge("Can't get version of APN database" + e + " return version=" + 208 Integer.toHexString(DATABASE_VERSION)); 209 return DATABASE_VERSION; 210 } finally { 211 parser.close(); 212 } 213 } 214 215 @Override onCreate(SQLiteDatabase db)216 public void onCreate(SQLiteDatabase db) { 217 if (DBG) log("dbh.onCreate:+ db=" + db); 218 createSimInfoTable(db); 219 createCarriersTable(db, CARRIERS_TABLE); 220 initDatabase(db); 221 if (DBG) log("dbh.onCreate:- db=" + db); 222 } 223 224 @Override onOpen(SQLiteDatabase db)225 public void onOpen(SQLiteDatabase db) { 226 if (VDBG) log("dbh.onOpen:+ db=" + db); 227 try { 228 // Try to access the table and create it if "no such table" 229 db.query(SIMINFO_TABLE, null, null, null, null, null, null); 230 if (DBG) log("dbh.onOpen: ok, queried table=" + SIMINFO_TABLE); 231 } catch (SQLiteException e) { 232 loge("Exception " + SIMINFO_TABLE + "e=" + e); 233 if (e.getMessage().startsWith("no such table")) { 234 createSimInfoTable(db); 235 } 236 } 237 try { 238 db.query(CARRIERS_TABLE, null, null, null, null, null, null); 239 if (DBG) log("dbh.onOpen: ok, queried table=" + CARRIERS_TABLE); 240 } catch (SQLiteException e) { 241 loge("Exception " + CARRIERS_TABLE + " e=" + e); 242 if (e.getMessage().startsWith("no such table")) { 243 createCarriersTable(db, CARRIERS_TABLE); 244 } 245 } 246 if (VDBG) log("dbh.onOpen:- db=" + db); 247 } 248 createSimInfoTable(SQLiteDatabase db)249 private void createSimInfoTable(SQLiteDatabase db) { 250 if (DBG) log("dbh.createSimInfoTable:+"); 251 db.execSQL("CREATE TABLE " + SIMINFO_TABLE + "(" 252 + SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," 253 + SubscriptionManager.ICC_ID + " TEXT NOT NULL," 254 + SubscriptionManager.SIM_SLOT_INDEX + " INTEGER DEFAULT " + SubscriptionManager.SIM_NOT_INSERTED + "," 255 + SubscriptionManager.DISPLAY_NAME + " TEXT," 256 + SubscriptionManager.CARRIER_NAME + " TEXT," 257 + SubscriptionManager.NAME_SOURCE + " INTEGER DEFAULT " + SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE + "," 258 + SubscriptionManager.COLOR + " INTEGER DEFAULT " + SubscriptionManager.COLOR_DEFAULT + "," 259 + SubscriptionManager.NUMBER + " TEXT," 260 + SubscriptionManager.DISPLAY_NUMBER_FORMAT + " INTEGER NOT NULL DEFAULT " + SubscriptionManager.DISPLAY_NUMBER_DEFAULT + "," 261 + SubscriptionManager.DATA_ROAMING + " INTEGER DEFAULT " + SubscriptionManager.DATA_ROAMING_DEFAULT + "," 262 + SubscriptionManager.MCC + " INTEGER DEFAULT 0," 263 + SubscriptionManager.MNC + " INTEGER DEFAULT 0," 264 + SubscriptionManager.SIM_PROVISIONING_STATUS + " INTEGER DEFAULT " + SubscriptionManager.SIM_PROVISIONED + "," 265 + SubscriptionManager.CB_EXTREME_THREAT_ALERT + " INTEGER DEFAULT 1," 266 + SubscriptionManager.CB_SEVERE_THREAT_ALERT + " INTEGER DEFAULT 1," 267 + SubscriptionManager.CB_AMBER_ALERT + " INTEGER DEFAULT 1," 268 + SubscriptionManager.CB_EMERGENCY_ALERT + " INTEGER DEFAULT 1," 269 + SubscriptionManager.CB_ALERT_SOUND_DURATION + " INTEGER DEFAULT 4," 270 + SubscriptionManager.CB_ALERT_REMINDER_INTERVAL + " INTEGER DEFAULT 0," 271 + SubscriptionManager.CB_ALERT_VIBRATE + " INTEGER DEFAULT 1," 272 + SubscriptionManager.CB_ALERT_SPEECH + " INTEGER DEFAULT 1," 273 + SubscriptionManager.CB_ETWS_TEST_ALERT + " INTEGER DEFAULT 0," 274 + SubscriptionManager.CB_CHANNEL_50_ALERT + " INTEGER DEFAULT 1," 275 + SubscriptionManager.CB_CMAS_TEST_ALERT + " INTEGER DEFAULT 0," 276 + SubscriptionManager.CB_OPT_OUT_DIALOG + " INTEGER DEFAULT 1" 277 + ");"); 278 if (DBG) log("dbh.createSimInfoTable:-"); 279 } 280 createCarriersTable(SQLiteDatabase db, String tableName)281 private void createCarriersTable(SQLiteDatabase db, String tableName) { 282 // Set up the database schema 283 if (DBG) log("dbh.createCarriersTable: " + tableName); 284 db.execSQL("CREATE TABLE " + tableName + 285 "(_id INTEGER PRIMARY KEY," + 286 NAME + " TEXT DEFAULT ''," + 287 NUMERIC + " TEXT DEFAULT ''," + 288 MCC + " TEXT DEFAULT ''," + 289 MNC + " TEXT DEFAULT ''," + 290 APN + " TEXT DEFAULT ''," + 291 USER + " TEXT DEFAULT ''," + 292 SERVER + " TEXT DEFAULT ''," + 293 PASSWORD + " TEXT DEFAULT ''," + 294 PROXY + " TEXT DEFAULT ''," + 295 PORT + " TEXT DEFAULT ''," + 296 MMSPROXY + " TEXT DEFAULT ''," + 297 MMSPORT + " TEXT DEFAULT ''," + 298 MMSC + " TEXT DEFAULT ''," + 299 AUTH_TYPE + " INTEGER DEFAULT -1," + 300 TYPE + " TEXT DEFAULT ''," + 301 CURRENT + " INTEGER," + 302 PROTOCOL + " TEXT DEFAULT 'IP'," + 303 ROAMING_PROTOCOL + " TEXT DEFAULT 'IP'," + 304 CARRIER_ENABLED + " BOOLEAN DEFAULT 1," + 305 BEARER + " INTEGER DEFAULT 0," + 306 BEARER_BITMASK + " INTEGER DEFAULT 0," + 307 MVNO_TYPE + " TEXT DEFAULT ''," + 308 MVNO_MATCH_DATA + " TEXT DEFAULT ''," + 309 SUBSCRIPTION_ID + " INTEGER DEFAULT " 310 + SubscriptionManager.INVALID_SUBSCRIPTION_ID + "," + 311 PROFILE_ID + " INTEGER DEFAULT 0," + 312 MODEM_COGNITIVE + " BOOLEAN DEFAULT 0," + 313 MAX_CONNS + " INTEGER DEFAULT 0," + 314 WAIT_TIME + " INTEGER DEFAULT 0," + 315 MAX_CONNS_TIME + " INTEGER DEFAULT 0," + 316 MTU + " INTEGER DEFAULT 0," + 317 EDITED + " INTEGER DEFAULT " + UNEDITED + "," + 318 USER_VISIBLE + " BOOLEAN DEFAULT 1," + 319 // Uniqueness collisions are used to trigger merge code so if a field is listed 320 // here it means we will accept both (user edited + new apn_conf definition) 321 // Columns not included in UNIQUE constraint: name, current, edited, 322 // user, server, password, authtype, type, protocol, roaming_protocol, sub_id, 323 // modem_cognitive, max_conns, wait_time, max_conns_time, mtu, bearer_bitmask, 324 // user_visible 325 "UNIQUE (" + TextUtils.join(", ", CARRIERS_UNIQUE_FIELDS) + "));"); 326 if (DBG) log("dbh.createCarriersTable:-"); 327 } 328 getChecksum(File file)329 private long getChecksum(File file) { 330 long checksum = -1; 331 try { 332 checksum = FileUtils.checksumCrc32(file); 333 if (DBG) log("Checksum for " + file.getAbsolutePath() + " is " + checksum); 334 } catch (FileNotFoundException e) { 335 loge("FileNotFoundException for " + file.getAbsolutePath() + ":" + e); 336 } catch (IOException e) { 337 loge("IOException for " + file.getAbsolutePath() + ":" + e); 338 } 339 return checksum; 340 } 341 getApnConfChecksum()342 private long getApnConfChecksum() { 343 SharedPreferences sp = mContext.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); 344 return sp.getLong(APN_CONF_CHECKSUM, -1); 345 } 346 setApnConfChecksum(long checksum)347 private void setApnConfChecksum(long checksum) { 348 SharedPreferences sp = mContext.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); 349 SharedPreferences.Editor editor = sp.edit(); 350 editor.putLong(APN_CONF_CHECKSUM, checksum); 351 editor.apply(); 352 } 353 getApnConfFile()354 private File getApnConfFile() { 355 // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system". 356 File confFile = new File(Environment.getRootDirectory(), PARTNER_APNS_PATH); 357 File oemConfFile = new File(Environment.getOemDirectory(), OEM_APNS_PATH); 358 File updatedConfFile = new File(Environment.getDataDirectory(), OTA_UPDATED_APNS_PATH); 359 confFile = getNewerFile(confFile, oemConfFile); 360 confFile = getNewerFile(confFile, updatedConfFile); 361 return confFile; 362 } 363 364 /** 365 * This function computes checksum for the file to be read and compares it against the 366 * last read file. DB needs to be updated only if checksum has changed, or old checksum does 367 * not exist. 368 * @return true if DB should be updated with new conf file, false otherwise 369 */ apnDbUpdateNeeded()370 private boolean apnDbUpdateNeeded() { 371 File confFile = getApnConfFile(); 372 long newChecksum = getChecksum(confFile); 373 long oldChecksum = getApnConfChecksum(); 374 if (DBG) log("newChecksum: " + newChecksum); 375 if (DBG) log("oldChecksum: " + oldChecksum); 376 if (newChecksum == oldChecksum) { 377 return false; 378 } else { 379 return true; 380 } 381 } 382 383 /** 384 * This function adds APNs from xml file(s) to db. The db may or may not be empty to begin 385 * with. 386 */ initDatabase(SQLiteDatabase db)387 private void initDatabase(SQLiteDatabase db) { 388 if (VDBG) log("dbh.initDatabase:+ db=" + db); 389 // Read internal APNS data 390 Resources r = mContext.getResources(); 391 XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns); 392 int publicversion = -1; 393 try { 394 XmlUtils.beginDocument(parser, "apns"); 395 publicversion = Integer.parseInt(parser.getAttributeValue(null, "version")); 396 loadApns(db, parser); 397 } catch (Exception e) { 398 loge("Got exception while loading APN database." + e); 399 } finally { 400 parser.close(); 401 } 402 403 // Read external APNS data (partner-provided) 404 XmlPullParser confparser = null; 405 File confFile = getApnConfFile(); 406 407 FileReader confreader = null; 408 if (DBG) log("confFile = " + confFile); 409 try { 410 confreader = new FileReader(confFile); 411 confparser = Xml.newPullParser(); 412 confparser.setInput(confreader); 413 XmlUtils.beginDocument(confparser, "apns"); 414 415 // Sanity check. Force internal version and confidential versions to agree 416 int confversion = Integer.parseInt(confparser.getAttributeValue(null, "version")); 417 if (publicversion != confversion) { 418 log("initDatabase: throwing exception due to version mismatch"); 419 throw new IllegalStateException("Internal APNS file version doesn't match " 420 + confFile.getAbsolutePath()); 421 } 422 423 loadApns(db, confparser); 424 } catch (FileNotFoundException e) { 425 // It's ok if the file isn't found. It means there isn't a confidential file 426 // Log.e(TAG, "File not found: '" + confFile.getAbsolutePath() + "'"); 427 } catch (Exception e) { 428 loge("initDatabase: Exception while parsing '" + confFile.getAbsolutePath() + "'" + 429 e); 430 } finally { 431 // Get rid of user/carrier deleted entries that are not present in apn xml file. 432 // Those entries have edited value USER_DELETED/CARRIER_DELETED. 433 if (VDBG) { 434 log("initDatabase: deleting USER_DELETED and replacing " 435 + "DELETED_BUT_PRESENT_IN_XML with DELETED"); 436 } 437 438 // Delete USER_DELETED 439 db.delete(CARRIERS_TABLE, IS_USER_DELETED + " or " + IS_CARRIER_DELETED, null); 440 441 // Change USER_DELETED_BUT_PRESENT_IN_XML to USER_DELETED 442 ContentValues cv = new ContentValues(); 443 cv.put(EDITED, USER_DELETED); 444 db.update(CARRIERS_TABLE, cv, IS_USER_DELETED_BUT_PRESENT_IN_XML, null); 445 446 // Change CARRIER_DELETED_BUT_PRESENT_IN_XML to CARRIER_DELETED 447 cv = new ContentValues(); 448 cv.put(EDITED, CARRIER_DELETED); 449 db.update(CARRIERS_TABLE, cv, IS_CARRIER_DELETED_BUT_PRESENT_IN_XML, null); 450 451 if (confreader != null) { 452 try { 453 confreader.close(); 454 } catch (IOException e) { 455 // do nothing 456 } 457 } 458 459 // Update the stored checksum 460 setApnConfChecksum(getChecksum(confFile)); 461 } 462 if (VDBG) log("dbh.initDatabase:- db=" + db); 463 464 } 465 getNewerFile(File sysApnFile, File altApnFile)466 private File getNewerFile(File sysApnFile, File altApnFile) { 467 if (altApnFile.exists()) { 468 // Alternate file exists. Use the newer one. 469 long altFileTime = altApnFile.lastModified(); 470 long currFileTime = sysApnFile.lastModified(); 471 if (DBG) log("APNs Timestamp: altFileTime = " + altFileTime + " currFileTime = " 472 + currFileTime); 473 474 // To get the latest version from OEM or System image 475 if (altFileTime > currFileTime) { 476 if (DBG) log("APNs Timestamp: Alternate image " + altApnFile.getPath() + 477 " is greater than System image"); 478 return altApnFile; 479 } 480 } else { 481 // No Apn in alternate image, so load it from system image. 482 if (DBG) log("No APNs in OEM image = " + altApnFile.getPath() + 483 " Load APNs from system image"); 484 } 485 return sysApnFile; 486 } 487 488 @Override onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)489 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 490 if (DBG) { 491 log("dbh.onUpgrade:+ db=" + db + " oldV=" + oldVersion + " newV=" + newVersion); 492 } 493 494 if (oldVersion < (5 << 16 | 6)) { 495 // 5 << 16 is the Database version and 6 in the xml version. 496 497 // This change adds a new authtype column to the database. 498 // The auth type column can have 4 values: 0 (None), 1 (PAP), 2 (CHAP) 499 // 3 (PAP or CHAP). To avoid breaking compatibility, with already working 500 // APNs, the unset value (-1) will be used. If the value is -1. 501 // the authentication will default to 0 (if no user / password) is specified 502 // or to 3. Currently, there have been no reported problems with 503 // pre-configured APNs and hence it is set to -1 for them. Similarly, 504 // if the user, has added a new APN, we set the authentication type 505 // to -1. 506 507 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 508 " ADD COLUMN authtype INTEGER DEFAULT -1;"); 509 510 oldVersion = 5 << 16 | 6; 511 } 512 if (oldVersion < (6 << 16 | 6)) { 513 // Add protcol fields to the APN. The XML file does not change. 514 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 515 " ADD COLUMN protocol TEXT DEFAULT IP;"); 516 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 517 " ADD COLUMN roaming_protocol TEXT DEFAULT IP;"); 518 oldVersion = 6 << 16 | 6; 519 } 520 if (oldVersion < (7 << 16 | 6)) { 521 // Add carrier_enabled, bearer fields to the APN. The XML file does not change. 522 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 523 " ADD COLUMN carrier_enabled BOOLEAN DEFAULT 1;"); 524 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 525 " ADD COLUMN bearer INTEGER DEFAULT 0;"); 526 oldVersion = 7 << 16 | 6; 527 } 528 if (oldVersion < (8 << 16 | 6)) { 529 // Add mvno_type, mvno_match_data fields to the APN. 530 // The XML file does not change. 531 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 532 " ADD COLUMN mvno_type TEXT DEFAULT '';"); 533 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 534 " ADD COLUMN mvno_match_data TEXT DEFAULT '';"); 535 oldVersion = 8 << 16 | 6; 536 } 537 if (oldVersion < (9 << 16 | 6)) { 538 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 539 " ADD COLUMN sub_id INTEGER DEFAULT " + 540 SubscriptionManager.INVALID_SUBSCRIPTION_ID + ";"); 541 oldVersion = 9 << 16 | 6; 542 } 543 if (oldVersion < (10 << 16 | 6)) { 544 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 545 " ADD COLUMN profile_id INTEGER DEFAULT 0;"); 546 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 547 " ADD COLUMN modem_cognitive BOOLEAN DEFAULT 0;"); 548 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 549 " ADD COLUMN max_conns INTEGER DEFAULT 0;"); 550 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 551 " ADD COLUMN wait_time INTEGER DEFAULT 0;"); 552 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 553 " ADD COLUMN max_conns_time INTEGER DEFAULT 0;"); 554 oldVersion = 10 << 16 | 6; 555 } 556 if (oldVersion < (11 << 16 | 6)) { 557 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 558 " ADD COLUMN mtu INTEGER DEFAULT 0;"); 559 oldVersion = 11 << 16 | 6; 560 } 561 if (oldVersion < (12 << 16 | 6)) { 562 try { 563 // Try to update the siminfo table. It might not be there. 564 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + 565 " ADD COLUMN " + SubscriptionManager.MCC + " INTEGER DEFAULT 0;"); 566 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + 567 " ADD COLUMN " + SubscriptionManager.MNC + " INTEGER DEFAULT 0;"); 568 } catch (SQLiteException e) { 569 if (DBG) { 570 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 571 " The table will get created in onOpen."); 572 } 573 } 574 oldVersion = 12 << 16 | 6; 575 } 576 if (oldVersion < (13 << 16 | 6)) { 577 try { 578 // Try to update the siminfo table. It might not be there. 579 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " + 580 SubscriptionManager.CARRIER_NAME + " TEXT DEFAULT '';"); 581 } catch (SQLiteException e) { 582 if (DBG) { 583 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 584 " The table will get created in onOpen."); 585 } 586 } 587 oldVersion = 13 << 16 | 6; 588 } 589 if (oldVersion < (14 << 16 | 6)) { 590 // Do nothing. This is to avoid recreating table twice. Table is anyway recreated 591 // for next version and that takes care of updates for this version as well. 592 // This version added a new column user_edited to carriers db. 593 } 594 if (oldVersion < (15 << 16 | 6)) { 595 // Most devices should be upgrading from version 13. On upgrade new db will be 596 // populated from the xml included in OTA but user and carrier edited/added entries 597 // need to be preserved. This new version also adds new columns EDITED and 598 // BEARER_BITMASK to the table. Upgrade steps from version 13 are: 599 // 1. preserve user and carrier added/edited APNs (by comparing against 600 // old-apns-conf.xml included in OTA) - done in preserveUserAndCarrierApns() 601 // 2. add new columns EDITED and BEARER_BITMASK (create a new table for that) - done 602 // in createCarriersTable() 603 // 3. copy over preserved APNs from old table to new table - done in 604 // copyPreservedApnsToNewTable() 605 // The only exception if upgrading from version 14 is that EDITED field is already 606 // present (but is called USER_EDITED) 607 /********************************************************************************* 608 * IMPORTANT NOTE: SINCE CARRIERS TABLE IS RECREATED HERE, IT WILL BE THE LATEST 609 * VERSION AFTER THIS. AS A RESULT ANY SUBSEQUENT UPDATES TO THE TABLE WILL FAIL 610 * (DUE TO COLUMN-ALREADY-EXISTS KIND OF EXCEPTION). ALL SUBSEQUENT UPDATES SHOULD 611 * HANDLE THAT GRACEFULLY. 612 *********************************************************************************/ 613 Cursor c; 614 String[] proj = {"_id"}; 615 if (VDBG) { 616 c = db.query(CARRIERS_TABLE, proj, null, null, null, null, null); 617 log("dbh.onUpgrade:- before upgrading total number of rows: " + c.getCount()); 618 } 619 620 // Compare db with old apns xml file so that any user or carrier edited/added 621 // entries can be preserved across upgrade 622 preserveUserAndCarrierApns(db); 623 624 c = db.query(CARRIERS_TABLE, null, null, null, null, null, null); 625 626 if (VDBG) { 627 log("dbh.onUpgrade:- after preserveUserAndCarrierApns() total number of " + 628 "rows: " + ((c == null) ? 0 : c.getCount())); 629 } 630 631 createCarriersTable(db, CARRIERS_TABLE_TMP); 632 633 copyPreservedApnsToNewTable(db, c); 634 c.close(); 635 636 db.execSQL("DROP TABLE IF EXISTS " + CARRIERS_TABLE); 637 638 db.execSQL("ALTER TABLE " + CARRIERS_TABLE_TMP + " rename to " + CARRIERS_TABLE + 639 ";"); 640 641 if (VDBG) { 642 c = db.query(CARRIERS_TABLE, proj, null, null, null, null, null); 643 log("dbh.onUpgrade:- after upgrading total number of rows: " + c.getCount()); 644 c.close(); 645 c = db.query(CARRIERS_TABLE, proj, IS_UNEDITED, null, null, null, null); 646 log("dbh.onUpgrade:- after upgrading total number of rows with " + IS_UNEDITED + 647 ": " + c.getCount()); 648 c.close(); 649 c = db.query(CARRIERS_TABLE, proj, IS_EDITED, null, null, null, null); 650 log("dbh.onUpgrade:- after upgrading total number of rows with " + IS_EDITED + 651 ": " + c.getCount()); 652 c.close(); 653 } 654 655 oldVersion = 15 << 16 | 6; 656 } 657 if (oldVersion < (16 << 16 | 6)) { 658 try { 659 // Try to update the siminfo table. It might not be there. 660 // These columns may already be present in which case execSQL will throw an 661 // exception 662 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 663 + SubscriptionManager.CB_EXTREME_THREAT_ALERT + " INTEGER DEFAULT 1;"); 664 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 665 + SubscriptionManager.CB_SEVERE_THREAT_ALERT + " INTEGER DEFAULT 1;"); 666 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 667 + SubscriptionManager.CB_AMBER_ALERT + " INTEGER DEFAULT 1;"); 668 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 669 + SubscriptionManager.CB_EMERGENCY_ALERT + " INTEGER DEFAULT 1;"); 670 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 671 + SubscriptionManager.CB_ALERT_SOUND_DURATION + " INTEGER DEFAULT 4;"); 672 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 673 + SubscriptionManager.CB_ALERT_REMINDER_INTERVAL + " INTEGER DEFAULT 0;"); 674 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 675 + SubscriptionManager.CB_ALERT_VIBRATE + " INTEGER DEFAULT 1;"); 676 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 677 + SubscriptionManager.CB_ALERT_SPEECH + " INTEGER DEFAULT 1;"); 678 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 679 + SubscriptionManager.CB_ETWS_TEST_ALERT + " INTEGER DEFAULT 0;"); 680 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 681 + SubscriptionManager.CB_CHANNEL_50_ALERT + " INTEGER DEFAULT 1;"); 682 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 683 + SubscriptionManager.CB_CMAS_TEST_ALERT + " INTEGER DEFAULT 0;"); 684 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 685 + SubscriptionManager.CB_OPT_OUT_DIALOG + " INTEGER DEFAULT 1;"); 686 } catch (SQLiteException e) { 687 if (DBG) { 688 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 689 " The table will get created in onOpen."); 690 } 691 } 692 oldVersion = 16 << 16 | 6; 693 } 694 if (oldVersion < (17 << 16 | 6)) { 695 Cursor c = null; 696 try { 697 c = db.query(CARRIERS_TABLE, null, null, null, null, null, null, 698 String.valueOf(1)); 699 if (c == null || c.getColumnIndex(USER_VISIBLE) == -1) { 700 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + " ADD COLUMN " + 701 USER_VISIBLE + " BOOLEAN DEFAULT 1;"); 702 } else { 703 if (DBG) { 704 log("onUpgrade skipping " + CARRIERS_TABLE + " upgrade. Column " + 705 USER_VISIBLE + " already exists."); 706 } 707 } 708 } finally { 709 if (c != null) { 710 c.close(); 711 } 712 } 713 oldVersion = 17 << 16 | 6; 714 } 715 if (oldVersion < (18 << 16 | 6)) { 716 try { 717 // Try to update the siminfo table. It might not be there. 718 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " + 719 SubscriptionManager.SIM_PROVISIONING_STATUS + " INTEGER DEFAULT " + 720 SubscriptionManager.SIM_PROVISIONED + ";"); 721 } catch (SQLiteException e) { 722 if (DBG) { 723 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 724 " The table will get created in onOpen."); 725 } 726 } 727 oldVersion = 18 << 16 | 6; 728 } 729 if (DBG) { 730 log("dbh.onUpgrade:- db=" + db + " oldV=" + oldVersion + " newV=" + newVersion); 731 } 732 } 733 preserveUserAndCarrierApns(SQLiteDatabase db)734 private void preserveUserAndCarrierApns(SQLiteDatabase db) { 735 if (VDBG) log("preserveUserAndCarrierApns"); 736 XmlPullParser confparser; 737 File confFile = new File(Environment.getRootDirectory(), OLD_APNS_PATH); 738 FileReader confreader = null; 739 try { 740 confreader = new FileReader(confFile); 741 confparser = Xml.newPullParser(); 742 confparser.setInput(confreader); 743 XmlUtils.beginDocument(confparser, "apns"); 744 745 deleteMatchingApns(db, confparser); 746 } catch (FileNotFoundException e) { 747 // This function is called only when upgrading db to version 15. Details about the 748 // upgrade are mentioned in onUpgrade(). This file missing means user/carrier added 749 // APNs cannot be preserved. Log an error message so that OEMs know they need to 750 // include old apns file for comparison. 751 loge("PRESERVEUSERANDCARRIERAPNS: " + OLD_APNS_PATH + 752 " NOT FOUND. IT IS NEEDED TO UPGRADE FROM OLDER VERSIONS OF APN " + 753 "DB WHILE PRESERVING USER/CARRIER ADDED/EDITED ENTRIES."); 754 } catch (Exception e) { 755 loge("preserveUserAndCarrierApns: Exception while parsing '" + 756 confFile.getAbsolutePath() + "'" + e); 757 } finally { 758 if (confreader != null) { 759 try { 760 confreader.close(); 761 } catch (IOException e) { 762 // do nothing 763 } 764 } 765 } 766 } 767 deleteMatchingApns(SQLiteDatabase db, XmlPullParser parser)768 private void deleteMatchingApns(SQLiteDatabase db, XmlPullParser parser) { 769 if (VDBG) log("deleteMatchingApns"); 770 if (parser != null) { 771 if (VDBG) log("deleteMatchingApns: parser != null"); 772 try { 773 XmlUtils.nextElement(parser); 774 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 775 ContentValues row = getRow(parser); 776 if (row == null) { 777 throw new XmlPullParserException("Expected 'apn' tag", parser, null); 778 } 779 deleteRow(db, row); 780 XmlUtils.nextElement(parser); 781 } 782 } catch (XmlPullParserException e) { 783 loge("deleteMatchingApns: Got XmlPullParserException while deleting apns." + e); 784 } catch (IOException e) { 785 loge("deleteMatchingApns: Got IOException while deleting apns." + e); 786 } catch (SQLException e) { 787 loge("deleteMatchingApns: Got SQLException while deleting apns." + e); 788 } 789 } 790 } 791 queryValFirst(String field)792 private String queryValFirst(String field) { 793 return field + "=?"; 794 } 795 queryVal(String field)796 private String queryVal(String field) { 797 return " and " + field + "=?"; 798 } 799 queryValOrNull(String field)800 private String queryValOrNull(String field) { 801 return " and (" + field + "=? or " + field + " is null)"; 802 } 803 queryVal2OrNull(String field)804 private String queryVal2OrNull(String field) { 805 return " and (" + field + "=? or " + field + "=? or " + field + " is null)"; 806 } 807 deleteRow(SQLiteDatabase db, ContentValues values)808 private void deleteRow(SQLiteDatabase db, ContentValues values) { 809 if (VDBG) log("deleteRow"); 810 String where = queryValFirst(NUMERIC) + 811 queryVal(MNC) + 812 queryVal(MNC) + 813 queryValOrNull(APN) + 814 queryValOrNull(USER) + 815 queryValOrNull(SERVER) + 816 queryValOrNull(PASSWORD) + 817 queryValOrNull(PROXY) + 818 queryValOrNull(PORT) + 819 queryValOrNull(MMSPROXY) + 820 queryValOrNull(MMSPORT) + 821 queryValOrNull(MMSC) + 822 queryValOrNull(AUTH_TYPE) + 823 queryValOrNull(TYPE) + 824 queryValOrNull(PROTOCOL) + 825 queryValOrNull(ROAMING_PROTOCOL) + 826 queryVal2OrNull(CARRIER_ENABLED) + 827 queryValOrNull(BEARER) + 828 queryValOrNull(MVNO_TYPE) + 829 queryValOrNull(MVNO_MATCH_DATA) + 830 queryValOrNull(PROFILE_ID) + 831 queryVal2OrNull(MODEM_COGNITIVE) + 832 queryValOrNull(MAX_CONNS) + 833 queryValOrNull(WAIT_TIME) + 834 queryValOrNull(MAX_CONNS_TIME) + 835 queryValOrNull(MTU); 836 String[] whereArgs = new String[29]; 837 int i = 0; 838 whereArgs[i++] = values.getAsString(NUMERIC); 839 whereArgs[i++] = values.getAsString(MCC); 840 whereArgs[i++] = values.getAsString(MNC); 841 whereArgs[i++] = values.getAsString(NAME); 842 whereArgs[i++] = values.containsKey(APN) ? 843 values.getAsString(APN) : ""; 844 whereArgs[i++] = values.containsKey(USER) ? 845 values.getAsString(USER) : ""; 846 whereArgs[i++] = values.containsKey(SERVER) ? 847 values.getAsString(SERVER) : ""; 848 whereArgs[i++] = values.containsKey(PASSWORD) ? 849 values.getAsString(PASSWORD) : ""; 850 whereArgs[i++] = values.containsKey(PROXY) ? 851 values.getAsString(PROXY) : ""; 852 whereArgs[i++] = values.containsKey(PORT) ? 853 values.getAsString(PORT) : ""; 854 whereArgs[i++] = values.containsKey(MMSPROXY) ? 855 values.getAsString(MMSPROXY) : ""; 856 whereArgs[i++] = values.containsKey(MMSPORT) ? 857 values.getAsString(MMSPORT) : ""; 858 whereArgs[i++] = values.containsKey(MMSC) ? 859 values.getAsString(MMSC) : ""; 860 whereArgs[i++] = values.containsKey(AUTH_TYPE) ? 861 values.getAsString(AUTH_TYPE) : "-1"; 862 whereArgs[i++] = values.containsKey(TYPE) ? 863 values.getAsString(TYPE) : ""; 864 whereArgs[i++] = values.containsKey(PROTOCOL) ? 865 values.getAsString(PROTOCOL) : "IP"; 866 whereArgs[i++] = values.containsKey(ROAMING_PROTOCOL) ? 867 values.getAsString(ROAMING_PROTOCOL) : "IP"; 868 869 if (values.containsKey(CARRIER_ENABLED) && 870 (values.getAsString(CARRIER_ENABLED). 871 equalsIgnoreCase("false") || 872 values.getAsString(CARRIER_ENABLED).equals("0"))) { 873 whereArgs[i++] = "false"; 874 whereArgs[i++] = "0"; 875 } else { 876 whereArgs[i++] = "true"; 877 whereArgs[i++] = "1"; 878 } 879 880 whereArgs[i++] = values.containsKey(BEARER) ? 881 values.getAsString(BEARER) : "0"; 882 whereArgs[i++] = values.containsKey(MVNO_TYPE) ? 883 values.getAsString(MVNO_TYPE) : ""; 884 whereArgs[i++] = values.containsKey(MVNO_MATCH_DATA) ? 885 values.getAsString(MVNO_MATCH_DATA) : ""; 886 whereArgs[i++] = values.containsKey(PROFILE_ID) ? 887 values.getAsString(PROFILE_ID) : "0"; 888 889 if (values.containsKey(MODEM_COGNITIVE) && 890 (values.getAsString(MODEM_COGNITIVE). 891 equalsIgnoreCase("true") || 892 values.getAsString(MODEM_COGNITIVE).equals("1"))) { 893 whereArgs[i++] = "true"; 894 whereArgs[i++] = "1"; 895 } else { 896 whereArgs[i++] = "false"; 897 whereArgs[i++] = "0"; 898 } 899 900 whereArgs[i++] = values.containsKey(MAX_CONNS) ? 901 values.getAsString(MAX_CONNS) : "0"; 902 whereArgs[i++] = values.containsKey(WAIT_TIME) ? 903 values.getAsString(WAIT_TIME) : "0"; 904 whereArgs[i++] = values.containsKey(MAX_CONNS_TIME) ? 905 values.getAsString(MAX_CONNS_TIME) : "0"; 906 whereArgs[i++] = values.containsKey(MTU) ? 907 values.getAsString(MTU) : "0"; 908 909 if (VDBG) { 910 log("deleteRow: where: " + where); 911 912 StringBuilder builder = new StringBuilder(); 913 for (String s : whereArgs) { 914 builder.append(s + ", "); 915 } 916 917 log("deleteRow: whereArgs: " + builder.toString()); 918 } 919 db.delete(CARRIERS_TABLE, where, whereArgs); 920 } 921 copyPreservedApnsToNewTable(SQLiteDatabase db, Cursor c)922 private void copyPreservedApnsToNewTable(SQLiteDatabase db, Cursor c) { 923 // Move entries from CARRIERS_TABLE to CARRIERS_TABLE_TMP 924 if (c != null) { 925 String[] persistApnsForPlmns = mContext.getResources().getStringArray( 926 R.array.persist_apns_for_plmn); 927 while (c.moveToNext()) { 928 ContentValues cv = new ContentValues(); 929 String val; 930 931 // Include only non-null values in cv so that null values can be replaced 932 // with default if there's a default value for the field 933 934 // String vals 935 getStringValueFromCursor(cv, c, NAME); 936 getStringValueFromCursor(cv, c, NUMERIC); 937 getStringValueFromCursor(cv, c, MCC); 938 getStringValueFromCursor(cv, c, MNC); 939 getStringValueFromCursor(cv, c, APN); 940 getStringValueFromCursor(cv, c, USER); 941 getStringValueFromCursor(cv, c, SERVER); 942 getStringValueFromCursor(cv, c, PASSWORD); 943 getStringValueFromCursor(cv, c, PROXY); 944 getStringValueFromCursor(cv, c, PORT); 945 getStringValueFromCursor(cv, c, MMSPROXY); 946 getStringValueFromCursor(cv, c, MMSPORT); 947 getStringValueFromCursor(cv, c, MMSC); 948 getStringValueFromCursor(cv, c, TYPE); 949 getStringValueFromCursor(cv, c, PROTOCOL); 950 getStringValueFromCursor(cv, c, ROAMING_PROTOCOL); 951 getStringValueFromCursor(cv, c, MVNO_TYPE); 952 getStringValueFromCursor(cv, c, MVNO_MATCH_DATA); 953 954 // bool/int vals 955 getIntValueFromCursor(cv, c, AUTH_TYPE); 956 getIntValueFromCursor(cv, c, CURRENT); 957 getIntValueFromCursor(cv, c, CARRIER_ENABLED); 958 getIntValueFromCursor(cv, c, BEARER); 959 getIntValueFromCursor(cv, c, SUBSCRIPTION_ID); 960 getIntValueFromCursor(cv, c, PROFILE_ID); 961 getIntValueFromCursor(cv, c, MODEM_COGNITIVE); 962 getIntValueFromCursor(cv, c, MAX_CONNS); 963 getIntValueFromCursor(cv, c, WAIT_TIME); 964 getIntValueFromCursor(cv, c, MAX_CONNS_TIME); 965 getIntValueFromCursor(cv, c, MTU); 966 967 // Change bearer to a bitmask 968 String bearerStr = c.getString(c.getColumnIndex(BEARER)); 969 if (!TextUtils.isEmpty(bearerStr)) { 970 int bearer_bitmask = ServiceState.getBitmaskForTech( 971 Integer.parseInt(bearerStr)); 972 cv.put(BEARER_BITMASK, bearer_bitmask); 973 } 974 975 int userEditedColumnIdx = c.getColumnIndex("user_edited"); 976 if (userEditedColumnIdx != -1) { 977 String user_edited = c.getString(userEditedColumnIdx); 978 if (!TextUtils.isEmpty(user_edited)) { 979 cv.put(EDITED, new Integer(user_edited)); 980 } 981 } else { 982 cv.put(EDITED, USER_EDITED); 983 } 984 985 // New EDITED column. Default value (UNEDITED) will 986 // be used for all rows except for non-mvno entries for plmns indicated 987 // by resource: those will be set to CARRIER_EDITED to preserve 988 // their current values 989 val = c.getString(c.getColumnIndex(NUMERIC)); 990 for (String s : persistApnsForPlmns) { 991 if (!TextUtils.isEmpty(val) && val.equals(s) && 992 (!cv.containsKey(MVNO_TYPE) || 993 TextUtils.isEmpty(cv.getAsString(MVNO_TYPE)))) { 994 if (userEditedColumnIdx == -1) { 995 cv.put(EDITED, CARRIER_EDITED); 996 } else { // if (oldVersion == 14) -- if db had user_edited column 997 if (cv.getAsInteger(EDITED) == USER_EDITED) { 998 cv.put(EDITED, CARRIER_EDITED); 999 } 1000 } 1001 1002 break; 1003 } 1004 } 1005 1006 try { 1007 db.insertWithOnConflict(CARRIERS_TABLE_TMP, null, cv, 1008 SQLiteDatabase.CONFLICT_ABORT); 1009 if (VDBG) { 1010 log("dbh.copyPreservedApnsToNewTable: db.insert returned >= 0; " + 1011 "insert successful for cv " + cv); 1012 } 1013 } catch (SQLException e) { 1014 if (VDBG) 1015 log("dbh.copyPreservedApnsToNewTable insertWithOnConflict exception " + 1016 e + " for cv " + cv); 1017 // Insertion failed which could be due to a conflict. Check if that is 1018 // the case and merge the entries 1019 Cursor oldRow = DatabaseHelper.selectConflictingRow(db, 1020 CARRIERS_TABLE_TMP, cv); 1021 if (oldRow != null) { 1022 ContentValues mergedValues = new ContentValues(); 1023 mergeFieldsAndUpdateDb(db, CARRIERS_TABLE_TMP, oldRow, cv, 1024 mergedValues, true, mContext); 1025 oldRow.close(); 1026 } 1027 } 1028 } 1029 } 1030 } 1031 getStringValueFromCursor(ContentValues cv, Cursor c, String key)1032 private void getStringValueFromCursor(ContentValues cv, Cursor c, String key) { 1033 String fromCursor = c.getString(c.getColumnIndex(key)); 1034 if (!TextUtils.isEmpty(fromCursor)) { 1035 cv.put(key, fromCursor); 1036 } 1037 } 1038 getIntValueFromCursor(ContentValues cv, Cursor c, String key)1039 private void getIntValueFromCursor(ContentValues cv, Cursor c, String key) { 1040 String fromCursor = c.getString(c.getColumnIndex(key)); 1041 if (!TextUtils.isEmpty(fromCursor)) { 1042 try { 1043 cv.put(key, new Integer(fromCursor)); 1044 } catch (NumberFormatException nfe) { 1045 // do nothing 1046 } 1047 } 1048 } 1049 1050 /** 1051 * Gets the next row of apn values. 1052 * 1053 * @param parser the parser 1054 * @return the row or null if it's not an apn 1055 */ getRow(XmlPullParser parser)1056 private ContentValues getRow(XmlPullParser parser) { 1057 if (!"apn".equals(parser.getName())) { 1058 return null; 1059 } 1060 1061 ContentValues map = new ContentValues(); 1062 1063 String mcc = parser.getAttributeValue(null, "mcc"); 1064 String mnc = parser.getAttributeValue(null, "mnc"); 1065 String numeric = mcc + mnc; 1066 1067 map.put(NUMERIC, numeric); 1068 map.put(MCC, mcc); 1069 map.put(MNC, mnc); 1070 map.put(NAME, parser.getAttributeValue(null, "carrier")); 1071 1072 // do not add NULL to the map so that default values can be inserted in db 1073 addStringAttribute(parser, "apn", map, APN); 1074 addStringAttribute(parser, "user", map, USER); 1075 addStringAttribute(parser, "server", map, SERVER); 1076 addStringAttribute(parser, "password", map, PASSWORD); 1077 addStringAttribute(parser, "proxy", map, PROXY); 1078 addStringAttribute(parser, "port", map, PORT); 1079 addStringAttribute(parser, "mmsproxy", map, MMSPROXY); 1080 addStringAttribute(parser, "mmsport", map, MMSPORT); 1081 addStringAttribute(parser, "mmsc", map, MMSC); 1082 addStringAttribute(parser, "type", map, TYPE); 1083 addStringAttribute(parser, "protocol", map, PROTOCOL); 1084 addStringAttribute(parser, "roaming_protocol", map, ROAMING_PROTOCOL); 1085 1086 addIntAttribute(parser, "authtype", map, AUTH_TYPE); 1087 addIntAttribute(parser, "bearer", map, BEARER); 1088 addIntAttribute(parser, "profile_id", map, PROFILE_ID); 1089 addIntAttribute(parser, "max_conns", map, MAX_CONNS); 1090 addIntAttribute(parser, "wait_time", map, WAIT_TIME); 1091 addIntAttribute(parser, "max_conns_time", map, MAX_CONNS_TIME); 1092 addIntAttribute(parser, "mtu", map, MTU); 1093 1094 1095 addBoolAttribute(parser, "carrier_enabled", map, CARRIER_ENABLED); 1096 addBoolAttribute(parser, "modem_cognitive", map, MODEM_COGNITIVE); 1097 addBoolAttribute(parser, "user_visible", map, USER_VISIBLE); 1098 1099 int bearerBitmask = 0; 1100 String bearerList = parser.getAttributeValue(null, "bearer_bitmask"); 1101 if (bearerList != null) { 1102 bearerBitmask = ServiceState.getBitmaskFromString(bearerList); 1103 } 1104 map.put(BEARER_BITMASK, bearerBitmask); 1105 1106 String mvno_type = parser.getAttributeValue(null, "mvno_type"); 1107 if (mvno_type != null) { 1108 String mvno_match_data = parser.getAttributeValue(null, "mvno_match_data"); 1109 if (mvno_match_data != null) { 1110 map.put(MVNO_TYPE, mvno_type); 1111 map.put(MVNO_MATCH_DATA, mvno_match_data); 1112 } 1113 } 1114 1115 return map; 1116 } 1117 addStringAttribute(XmlPullParser parser, String att, ContentValues map, String key)1118 private void addStringAttribute(XmlPullParser parser, String att, 1119 ContentValues map, String key) { 1120 String val = parser.getAttributeValue(null, att); 1121 if (val != null) { 1122 map.put(key, val); 1123 } 1124 } 1125 addIntAttribute(XmlPullParser parser, String att, ContentValues map, String key)1126 private void addIntAttribute(XmlPullParser parser, String att, 1127 ContentValues map, String key) { 1128 String val = parser.getAttributeValue(null, att); 1129 if (val != null) { 1130 map.put(key, Integer.parseInt(val)); 1131 } 1132 } 1133 addBoolAttribute(XmlPullParser parser, String att, ContentValues map, String key)1134 private void addBoolAttribute(XmlPullParser parser, String att, 1135 ContentValues map, String key) { 1136 String val = parser.getAttributeValue(null, att); 1137 if (val != null) { 1138 map.put(key, Boolean.parseBoolean(val)); 1139 } 1140 } 1141 1142 /* 1143 * Loads apns from xml file into the database 1144 * 1145 * @param db the sqlite database to write to 1146 * @param parser the xml parser 1147 * 1148 */ loadApns(SQLiteDatabase db, XmlPullParser parser)1149 private void loadApns(SQLiteDatabase db, XmlPullParser parser) { 1150 if (parser != null) { 1151 try { 1152 db.beginTransaction(); 1153 XmlUtils.nextElement(parser); 1154 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 1155 ContentValues row = getRow(parser); 1156 if (row == null) { 1157 throw new XmlPullParserException("Expected 'apn' tag", parser, null); 1158 } 1159 insertAddingDefaults(db, row); 1160 XmlUtils.nextElement(parser); 1161 } 1162 db.setTransactionSuccessful(); 1163 } catch (XmlPullParserException e) { 1164 loge("Got XmlPullParserException while loading apns." + e); 1165 } catch (IOException e) { 1166 loge("Got IOException while loading apns." + e); 1167 } catch (SQLException e) { 1168 loge("Got SQLException while loading apns." + e); 1169 } finally { 1170 db.endTransaction(); 1171 } 1172 } 1173 } 1174 setDefaultValue(ContentValues values)1175 static public ContentValues setDefaultValue(ContentValues values) { 1176 if (!values.containsKey(SUBSCRIPTION_ID)) { 1177 int subId = SubscriptionManager.getDefaultSubscriptionId(); 1178 values.put(SUBSCRIPTION_ID, subId); 1179 } 1180 1181 return values; 1182 } 1183 insertAddingDefaults(SQLiteDatabase db, ContentValues row)1184 private void insertAddingDefaults(SQLiteDatabase db, ContentValues row) { 1185 row = setDefaultValue(row); 1186 try { 1187 db.insertWithOnConflict(CARRIERS_TABLE, null, row, SQLiteDatabase.CONFLICT_ABORT); 1188 if (VDBG) log("dbh.insertAddingDefaults: db.insert returned >= 0; insert " + 1189 "successful for cv " + row); 1190 } catch (SQLException e) { 1191 if (VDBG) log("dbh.insertAddingDefaults: exception " + e); 1192 // Insertion failed which could be due to a conflict. Check if that is the case and 1193 // update edited field accordingly. 1194 // Search for the exact same entry and update edited field. 1195 // If it is USER_EDITED/CARRIER_EDITED change it to UNEDITED, 1196 // and if USER/CARRIER_DELETED change it to USER/CARRIER_DELETED_BUT_PRESENT_IN_XML. 1197 Cursor oldRow = selectConflictingRow(db, CARRIERS_TABLE, row); 1198 if (oldRow != null) { 1199 // Update the row 1200 ContentValues mergedValues = new ContentValues(); 1201 int edited = oldRow.getInt(oldRow.getColumnIndex(EDITED)); 1202 int old_edited = edited; 1203 if (edited != UNEDITED) { 1204 if (edited == USER_DELETED) { 1205 // USER_DELETED_BUT_PRESENT_IN_XML indicates entry has been deleted 1206 // by user but present in apn xml file. 1207 edited = USER_DELETED_BUT_PRESENT_IN_XML; 1208 } else if (edited == CARRIER_DELETED) { 1209 // CARRIER_DELETED_BUT_PRESENT_IN_XML indicates entry has been deleted 1210 // by user but present in apn xml file. 1211 edited = CARRIER_DELETED_BUT_PRESENT_IN_XML; 1212 } 1213 mergedValues.put(EDITED, edited); 1214 } 1215 1216 mergeFieldsAndUpdateDb(db, CARRIERS_TABLE, oldRow, row, mergedValues, false, 1217 mContext); 1218 1219 if (VDBG) log("dbh.insertAddingDefaults: old edited = " + old_edited 1220 + " new edited = " + edited); 1221 1222 oldRow.close(); 1223 } 1224 } 1225 } 1226 mergeFieldsAndUpdateDb(SQLiteDatabase db, String table, Cursor oldRow, ContentValues newRow, ContentValues mergedValues, boolean onUpgrade, Context context)1227 public static void mergeFieldsAndUpdateDb(SQLiteDatabase db, String table, Cursor oldRow, 1228 ContentValues newRow, ContentValues mergedValues, 1229 boolean onUpgrade, Context context) { 1230 if (newRow.containsKey(TYPE)) { 1231 // Merge the types 1232 String oldType = oldRow.getString(oldRow.getColumnIndex(TYPE)); 1233 String newType = newRow.getAsString(TYPE); 1234 1235 if (!oldType.equalsIgnoreCase(newType)) { 1236 if (oldType.equals("") || newType.equals("")) { 1237 newRow.put(TYPE, ""); 1238 } else { 1239 String[] oldTypes = oldType.toLowerCase().split(","); 1240 String[] newTypes = newType.toLowerCase().split(","); 1241 1242 if (VDBG) { 1243 log("mergeFieldsAndUpdateDb: Calling separateRowsNeeded() oldType=" + 1244 oldType + " old bearer=" + oldRow.getInt(oldRow.getColumnIndex( 1245 BEARER_BITMASK)) + 1246 " old profile_id=" + oldRow.getInt(oldRow.getColumnIndex( 1247 PROFILE_ID)) + 1248 " newRow " + newRow); 1249 } 1250 1251 // If separate rows are needed, do not need to merge any further 1252 if (separateRowsNeeded(db, table, oldRow, newRow, context, oldTypes, 1253 newTypes)) { 1254 if (VDBG) log("mergeFieldsAndUpdateDb: separateRowsNeeded() returned " + 1255 "true"); 1256 return; 1257 } 1258 1259 // Merge the 2 types 1260 ArrayList<String> mergedTypes = new ArrayList<String>(); 1261 mergedTypes.addAll(Arrays.asList(oldTypes)); 1262 for (String s : newTypes) { 1263 if (!mergedTypes.contains(s.trim())) { 1264 mergedTypes.add(s); 1265 } 1266 } 1267 StringBuilder mergedType = new StringBuilder(); 1268 for (int i = 0; i < mergedTypes.size(); i++) { 1269 mergedType.append((i == 0 ? "" : ",") + mergedTypes.get(i)); 1270 } 1271 newRow.put(TYPE, mergedType.toString()); 1272 } 1273 } 1274 mergedValues.put(TYPE, newRow.getAsString( 1275 TYPE)); 1276 } 1277 1278 if (newRow.containsKey(BEARER_BITMASK)) { 1279 int oldBearer = oldRow.getInt(oldRow.getColumnIndex(BEARER_BITMASK)); 1280 int newBearer = newRow.getAsInteger(BEARER_BITMASK); 1281 if (oldBearer != newBearer) { 1282 if (oldBearer == 0 || newBearer == 0) { 1283 newRow.put(BEARER_BITMASK, 0); 1284 } else { 1285 newRow.put(BEARER_BITMASK, (oldBearer | newBearer)); 1286 } 1287 } 1288 mergedValues.put(BEARER_BITMASK, newRow.getAsInteger(BEARER_BITMASK)); 1289 } 1290 1291 if (!onUpgrade) { 1292 mergedValues.putAll(newRow); 1293 } 1294 1295 if (mergedValues.size() > 0) { 1296 db.update(table, mergedValues, "_id=" + oldRow.getInt(oldRow.getColumnIndex("_id")), 1297 null); 1298 } 1299 } 1300 separateRowsNeeded(SQLiteDatabase db, String table, Cursor oldRow, ContentValues newRow, Context context, String[] oldTypes, String[] newTypes)1301 private static boolean separateRowsNeeded(SQLiteDatabase db, String table, Cursor oldRow, 1302 ContentValues newRow, Context context, 1303 String[] oldTypes, String[] newTypes) { 1304 // If this APN falls under persist_apns_for_plmn, and the 1305 // only difference between old type and new type is that one has dun, and 1306 // the APNs have profile_id 0 or not set, then set the profile_id to 1 for 1307 // the dun APN/remove dun from type. This will ensure both oldRow and newRow exist 1308 // separately in db. 1309 1310 boolean match = false; 1311 1312 // Check if APN falls under persist_apns_for_plmn 1313 String[] persistApnsForPlmns = context.getResources().getStringArray( 1314 R.array.persist_apns_for_plmn); 1315 for (String s : persistApnsForPlmns) { 1316 if (s.equalsIgnoreCase(newRow.getAsString(NUMERIC))) { 1317 match = true; 1318 break; 1319 } 1320 } 1321 1322 if (!match) return false; 1323 1324 // APN falls under persist_apns_for_plmn 1325 // Check if only difference between old type and new type is that 1326 // one has dun 1327 ArrayList<String> oldTypesAl = new ArrayList<String>(Arrays.asList(oldTypes)); 1328 ArrayList<String> newTypesAl = new ArrayList<String>(Arrays.asList(newTypes)); 1329 ArrayList<String> listWithDun = null; 1330 ArrayList<String> listWithoutDun = null; 1331 boolean dunInOld = false; 1332 if (oldTypesAl.size() == newTypesAl.size() + 1) { 1333 listWithDun = oldTypesAl; 1334 listWithoutDun = newTypesAl; 1335 dunInOld = true; 1336 } else if (oldTypesAl.size() + 1 == newTypesAl.size()) { 1337 listWithDun = newTypesAl; 1338 listWithoutDun = oldTypesAl; 1339 } else { 1340 return false; 1341 } 1342 1343 if (listWithDun.contains("dun") && !listWithoutDun.contains("dun")) { 1344 listWithoutDun.add("dun"); 1345 if (!listWithDun.containsAll(listWithoutDun)) { 1346 return false; 1347 } 1348 1349 // Only difference between old type and new type is that 1350 // one has dun 1351 // Check if profile_id is 0/not set 1352 if (oldRow.getInt(oldRow.getColumnIndex(PROFILE_ID)) == 0) { 1353 if (dunInOld) { 1354 // Update oldRow to remove dun from its type field 1355 ContentValues updateOldRow = new ContentValues(); 1356 StringBuilder sb = new StringBuilder(); 1357 boolean first = true; 1358 for (String s : listWithDun) { 1359 if (!s.equalsIgnoreCase("dun")) { 1360 sb.append(first ? s : "," + s); 1361 first = false; 1362 } 1363 } 1364 String updatedType = sb.toString(); 1365 if (VDBG) { 1366 log("separateRowsNeeded: updating type in oldRow to " + updatedType); 1367 } 1368 updateOldRow.put(TYPE, updatedType); 1369 db.update(table, updateOldRow, 1370 "_id=" + oldRow.getInt(oldRow.getColumnIndex("_id")), null); 1371 return true; 1372 } else { 1373 if (VDBG) log("separateRowsNeeded: adding profile id 1 to newRow"); 1374 // Update newRow to set profile_id to 1 1375 newRow.put(PROFILE_ID, new Integer(1)); 1376 } 1377 } else { 1378 return false; 1379 } 1380 1381 // If match was found, both oldRow and newRow need to exist 1382 // separately in db. Add newRow to db. 1383 try { 1384 db.insertWithOnConflict(table, null, newRow, SQLiteDatabase.CONFLICT_REPLACE); 1385 if (VDBG) log("separateRowsNeeded: added newRow with profile id 1 to db"); 1386 return true; 1387 } catch (SQLException e) { 1388 loge("Exception on trying to add new row after updating profile_id"); 1389 } 1390 } 1391 1392 return false; 1393 } 1394 selectConflictingRow(SQLiteDatabase db, String table, ContentValues row)1395 public static Cursor selectConflictingRow(SQLiteDatabase db, String table, 1396 ContentValues row) { 1397 // Conflict is possible only when numeric, mcc, mnc (fields without any default value) 1398 // are set in the new row 1399 if (!row.containsKey(NUMERIC) || !row.containsKey(MCC) || !row.containsKey(MNC)) { 1400 loge("dbh.selectConflictingRow: called for non-conflicting row: " + row); 1401 return null; 1402 } 1403 1404 String[] columns = { "_id", 1405 TYPE, 1406 EDITED, 1407 BEARER_BITMASK, 1408 PROFILE_ID }; 1409 String selection = TextUtils.join("=? AND ", CARRIERS_UNIQUE_FIELDS) + "=?"; 1410 int i = 0; 1411 String[] selectionArgs = new String[14]; 1412 selectionArgs[i++] = row.getAsString(NUMERIC); 1413 selectionArgs[i++] = row.getAsString(MCC); 1414 selectionArgs[i++] = row.getAsString(MNC); 1415 selectionArgs[i++] = row.containsKey(APN) ? row.getAsString(APN) : ""; 1416 selectionArgs[i++] = row.containsKey(PROXY) ? row.getAsString(PROXY) : ""; 1417 selectionArgs[i++] = row.containsKey(PORT) ? row.getAsString(PORT) : ""; 1418 selectionArgs[i++] = row.containsKey(MMSPROXY) ? row.getAsString(MMSPROXY) : ""; 1419 selectionArgs[i++] = row.containsKey(MMSPORT) ? row.getAsString(MMSPORT) : ""; 1420 selectionArgs[i++] = row.containsKey(MMSC) ? row.getAsString(MMSC) : ""; 1421 selectionArgs[i++] = row.containsKey(CARRIER_ENABLED) && 1422 (row.getAsString(CARRIER_ENABLED).equals("0") || 1423 row.getAsString(CARRIER_ENABLED).equals("false")) ? 1424 "0" : "1"; 1425 selectionArgs[i++] = row.containsKey(BEARER) ? row.getAsString(BEARER) : "0"; 1426 selectionArgs[i++] = row.containsKey(MVNO_TYPE) ? row.getAsString(MVNO_TYPE) : ""; 1427 selectionArgs[i++] = row.containsKey(MVNO_MATCH_DATA) ? 1428 row.getAsString(MVNO_MATCH_DATA) : ""; 1429 selectionArgs[i++] = row.containsKey(PROFILE_ID) ? row.getAsString(PROFILE_ID) : "0"; 1430 1431 Cursor c = db.query(table, columns, selection, selectionArgs, null, null, null); 1432 1433 if (c != null) { 1434 if (c.getCount() == 1) { 1435 if (VDBG) log("dbh.selectConflictingRow: " + c.getCount() + " conflicting " + 1436 "row found"); 1437 if (c.moveToFirst()) { 1438 return c; 1439 } else { 1440 loge("dbh.selectConflictingRow: moveToFirst() failed"); 1441 } 1442 } else { 1443 loge("dbh.selectConflictingRow: Expected 1 but found " + c.getCount() + 1444 " matching rows found for cv " + row); 1445 } 1446 c.close(); 1447 } else { 1448 loge("dbh.selectConflictingRow: Error - c is null; no matching row found for " + 1449 "cv " + row); 1450 } 1451 1452 return null; 1453 } 1454 } 1455 1456 @Override onCreate()1457 public boolean onCreate() { 1458 mOpenHelper = new DatabaseHelper(getContext()); 1459 1460 // Call getReadableDatabase() to make sure onUpgrade is called 1461 if (VDBG) log("onCreate: calling getReadableDatabase to trigger onUpgrade"); 1462 SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 1463 1464 // Update APN db on build update 1465 String newBuildId = SystemProperties.get("ro.build.id", null); 1466 if (!TextUtils.isEmpty(newBuildId)) { 1467 // Check if build id has changed 1468 SharedPreferences sp = getContext().getSharedPreferences(BUILD_ID_FILE, 1469 Context.MODE_PRIVATE); 1470 String oldBuildId = sp.getString(RO_BUILD_ID, ""); 1471 if (!newBuildId.equals(oldBuildId)) { 1472 if (DBG) log("onCreate: build id changed from " + oldBuildId + " to " + 1473 newBuildId); 1474 1475 // Get rid of old preferred apn shared preferences 1476 SubscriptionManager sm = SubscriptionManager.from(getContext()); 1477 if (sm != null) { 1478 List<SubscriptionInfo> subInfoList = sm.getAllSubscriptionInfoList(); 1479 for (SubscriptionInfo subInfo : subInfoList) { 1480 SharedPreferences spPrefFile = getContext().getSharedPreferences( 1481 PREF_FILE_APN + subInfo.getSubscriptionId(), Context.MODE_PRIVATE); 1482 if (spPrefFile != null) { 1483 SharedPreferences.Editor editor = spPrefFile.edit(); 1484 editor.clear(); 1485 editor.apply(); 1486 } 1487 } 1488 } 1489 1490 // Update APN DB 1491 updateApnDb(); 1492 } else { 1493 if (VDBG) log("onCreate: build id did not change: " + oldBuildId); 1494 } 1495 sp.edit().putString(RO_BUILD_ID, newBuildId).apply(); 1496 } else { 1497 if (VDBG) log("onCreate: newBuildId is empty"); 1498 } 1499 1500 if (VDBG) log("onCreate:- ret true"); 1501 return true; 1502 } 1503 setPreferredApnId(Long id, int subId)1504 private void setPreferredApnId(Long id, int subId) { 1505 SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_APN, 1506 Context.MODE_PRIVATE); 1507 SharedPreferences.Editor editor = sp.edit(); 1508 editor.putLong(COLUMN_APN_ID + subId, id != null ? id.longValue() : INVALID_APN_ID); 1509 editor.apply(); 1510 // remove saved apn if apnId is invalid 1511 if (id == null || id.longValue() == INVALID_APN_ID) { 1512 deletePreferredApn(subId); 1513 } 1514 } 1515 getPreferredApnId(int subId, boolean checkApnSp)1516 private long getPreferredApnId(int subId, boolean checkApnSp) { 1517 SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_APN, 1518 Context.MODE_PRIVATE); 1519 long apnId = sp.getLong(COLUMN_APN_ID + subId, INVALID_APN_ID); 1520 if (apnId == INVALID_APN_ID && checkApnSp) { 1521 apnId = getPreferredApnIdFromApn(subId); 1522 if (apnId != INVALID_APN_ID) { 1523 setPreferredApnId(apnId, subId); 1524 deletePreferredApn(subId); 1525 } 1526 } 1527 return apnId; 1528 } 1529 deletePreferredApnId()1530 private void deletePreferredApnId() { 1531 SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_APN, 1532 Context.MODE_PRIVATE); 1533 // before deleting, save actual preferred apns (not the ids) in a separate SP 1534 Map<String, ?> allPrefApnId = sp.getAll(); 1535 for (String key : allPrefApnId.keySet()) { 1536 // extract subId from key by removing COLUMN_APN_ID 1537 try { 1538 int subId = Integer.parseInt(key.replace(COLUMN_APN_ID, "")); 1539 long apnId = getPreferredApnId(subId, false); 1540 if (apnId != INVALID_APN_ID) { 1541 setPreferredApn(apnId, subId); 1542 } 1543 } catch (Exception e) { 1544 loge("Skipping over key " + key + " due to exception " + e); 1545 } 1546 } 1547 SharedPreferences.Editor editor = sp.edit(); 1548 editor.clear(); 1549 editor.apply(); 1550 } 1551 setPreferredApn(Long id, int subId)1552 private void setPreferredApn(Long id, int subId) { 1553 log("setPreferredApn: _id " + id + " subId " + subId); 1554 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 1555 // query all unique fields from id 1556 String[] proj = CARRIERS_UNIQUE_FIELDS.toArray(new String[CARRIERS_UNIQUE_FIELDS.size()]); 1557 Cursor c = db.query(CARRIERS_TABLE, proj, "_id=" + id, null, null, null, null); 1558 if (c != null) { 1559 if (c.getCount() == 1) { 1560 c.moveToFirst(); 1561 SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_FULL_APN, 1562 Context.MODE_PRIVATE); 1563 SharedPreferences.Editor editor = sp.edit(); 1564 // store values of all unique fields to SP 1565 for (String key : CARRIERS_UNIQUE_FIELDS) { 1566 editor.putString(key + subId, c.getString(c.getColumnIndex(key))); 1567 } 1568 // also store the version number 1569 editor.putString(DB_VERSION_KEY + subId, "" + DATABASE_VERSION); 1570 editor.apply(); 1571 } else { 1572 log("setPreferredApn: # matching APNs found " + c.getCount()); 1573 } 1574 c.close(); 1575 } else { 1576 log("setPreferredApn: No matching APN found"); 1577 } 1578 } 1579 getPreferredApnIdFromApn(int subId)1580 private long getPreferredApnIdFromApn(int subId) { 1581 log("getPreferredApnIdFromApn: for subId " + subId); 1582 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 1583 String where = TextUtils.join("=? and ", CARRIERS_UNIQUE_FIELDS) + "=?"; 1584 String[] whereArgs = new String[CARRIERS_UNIQUE_FIELDS.size()]; 1585 SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_FULL_APN, 1586 Context.MODE_PRIVATE); 1587 long apnId = INVALID_APN_ID; 1588 int i = 0; 1589 for (String key : CARRIERS_UNIQUE_FIELDS) { 1590 whereArgs[i] = sp.getString(key + subId, null); 1591 if (whereArgs[i] == null) { 1592 return INVALID_APN_ID; 1593 } 1594 i++; 1595 } 1596 Cursor c = db.query(CARRIERS_TABLE, new String[]{"_id"}, where, whereArgs, null, null, 1597 null); 1598 if (c != null) { 1599 if (c.getCount() == 1) { 1600 c.moveToFirst(); 1601 apnId = c.getInt(c.getColumnIndex("_id")); 1602 } else { 1603 log("getPreferredApnIdFromApn: returning INVALID. # matching APNs found " + 1604 c.getCount()); 1605 } 1606 c.close(); 1607 } else { 1608 log("getPreferredApnIdFromApn: returning INVALID. No matching APN found"); 1609 } 1610 return apnId; 1611 } 1612 deletePreferredApn(int subId)1613 private void deletePreferredApn(int subId) { 1614 log("deletePreferredApn: for subId " + subId); 1615 SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_FULL_APN, 1616 Context.MODE_PRIVATE); 1617 if (sp.contains(DB_VERSION_KEY + subId)) { 1618 log("deletePreferredApn: apn is stored. Deleting it now for subId " + subId); 1619 SharedPreferences.Editor editor = sp.edit(); 1620 editor.remove(DB_VERSION_KEY + subId); 1621 for (String key : CARRIERS_UNIQUE_FIELDS) { 1622 editor.remove(key + subId); 1623 } 1624 editor.remove(DB_VERSION_KEY + subId); 1625 editor.apply(); 1626 } 1627 } 1628 1629 @Override query(Uri url, String[] projectionIn, String selection, String[] selectionArgs, String sort)1630 public synchronized Cursor query(Uri url, String[] projectionIn, String selection, 1631 String[] selectionArgs, String sort) { 1632 if (VDBG) log("query: url=" + url + ", projectionIn=" + projectionIn + ", selection=" 1633 + selection + "selectionArgs=" + selectionArgs + ", sort=" + sort); 1634 TelephonyManager mTelephonyManager = 1635 (TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE); 1636 int subId = SubscriptionManager.getDefaultSubscriptionId(); 1637 String subIdString; 1638 SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 1639 qb.setStrict(true); // a little protection from injection attacks 1640 qb.setTables(CARRIERS_TABLE); 1641 1642 int match = s_urlMatcher.match(url); 1643 switch (match) { 1644 case URL_TELEPHONY_USING_SUBID: { 1645 subIdString = url.getLastPathSegment(); 1646 try { 1647 subId = Integer.parseInt(subIdString); 1648 } catch (NumberFormatException e) { 1649 loge("NumberFormatException" + e); 1650 return null; 1651 } 1652 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 1653 qb.appendWhere(NUMERIC + " = '" + mTelephonyManager.getSimOperator(subId) + "'"); 1654 // FIXME alter the selection to pass subId 1655 // selection = selection + "and subId = " 1656 } 1657 // intentional fall through from above case 1658 // do nothing 1659 case URL_TELEPHONY: { 1660 break; 1661 } 1662 1663 case URL_CURRENT_USING_SUBID: { 1664 subIdString = url.getLastPathSegment(); 1665 try { 1666 subId = Integer.parseInt(subIdString); 1667 } catch (NumberFormatException e) { 1668 loge("NumberFormatException" + e); 1669 return null; 1670 } 1671 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 1672 // FIXME alter the selection to pass subId 1673 // selection = selection + "and subId = " 1674 } 1675 //intentional fall through from above case 1676 case URL_CURRENT: { 1677 qb.appendWhere("current IS NOT NULL"); 1678 // do not ignore the selection since MMS may use it. 1679 //selection = null; 1680 break; 1681 } 1682 1683 case URL_ID: { 1684 qb.appendWhere("_id = " + url.getPathSegments().get(1)); 1685 break; 1686 } 1687 1688 case URL_PREFERAPN_USING_SUBID: 1689 case URL_PREFERAPN_NO_UPDATE_USING_SUBID: { 1690 subIdString = url.getLastPathSegment(); 1691 try { 1692 subId = Integer.parseInt(subIdString); 1693 } catch (NumberFormatException e) { 1694 loge("NumberFormatException" + e); 1695 return null; 1696 } 1697 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 1698 } 1699 //intentional fall through from above case 1700 case URL_PREFERAPN: 1701 case URL_PREFERAPN_NO_UPDATE: { 1702 qb.appendWhere("_id = " + getPreferredApnId(subId, true)); 1703 break; 1704 } 1705 1706 case URL_SIMINFO: { 1707 qb.setTables(SIMINFO_TABLE); 1708 break; 1709 } 1710 1711 default: { 1712 return null; 1713 } 1714 } 1715 1716 if (match != URL_SIMINFO) { 1717 if (projectionIn != null) { 1718 for (String column : projectionIn) { 1719 if (TYPE.equals(column) || 1720 MMSC.equals(column) || 1721 MMSPROXY.equals(column) || 1722 MMSPORT.equals(column) || 1723 APN.equals(column)) { 1724 // noop 1725 } else { 1726 checkPermission(); 1727 break; 1728 } 1729 } 1730 } else { 1731 // null returns all columns, so need permission check 1732 checkPermission(); 1733 } 1734 } 1735 1736 SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 1737 Cursor ret = null; 1738 try { 1739 // Exclude entries marked deleted 1740 if (CARRIERS_TABLE.equals(qb.getTables())) { 1741 if (TextUtils.isEmpty(selection)) { 1742 selection = ""; 1743 } else { 1744 selection += " and "; 1745 } 1746 selection += IS_NOT_USER_DELETED + " and " + 1747 IS_NOT_USER_DELETED_BUT_PRESENT_IN_XML + " and " + 1748 IS_NOT_CARRIER_DELETED + " and " + 1749 IS_NOT_CARRIER_DELETED_BUT_PRESENT_IN_XML; 1750 if (VDBG) log("query: selection modified to " + selection); 1751 } 1752 ret = qb.query(db, projectionIn, selection, selectionArgs, null, null, sort); 1753 } catch (SQLException e) { 1754 loge("got exception when querying: " + e); 1755 } 1756 if (ret != null) 1757 ret.setNotificationUri(getContext().getContentResolver(), url); 1758 return ret; 1759 } 1760 1761 @Override getType(Uri url)1762 public String getType(Uri url) 1763 { 1764 switch (s_urlMatcher.match(url)) { 1765 case URL_TELEPHONY: 1766 case URL_TELEPHONY_USING_SUBID: 1767 return "vnd.android.cursor.dir/telephony-carrier"; 1768 1769 case URL_ID: 1770 return "vnd.android.cursor.item/telephony-carrier"; 1771 1772 case URL_PREFERAPN_USING_SUBID: 1773 case URL_PREFERAPN_NO_UPDATE_USING_SUBID: 1774 case URL_PREFERAPN: 1775 case URL_PREFERAPN_NO_UPDATE: 1776 return "vnd.android.cursor.item/telephony-carrier"; 1777 1778 default: 1779 throw new IllegalArgumentException("Unknown URL " + url); 1780 } 1781 } 1782 1783 @Override insert(Uri url, ContentValues initialValues)1784 public synchronized Uri insert(Uri url, ContentValues initialValues) 1785 { 1786 Uri result = null; 1787 int subId = SubscriptionManager.getDefaultSubscriptionId(); 1788 1789 checkPermission(); 1790 1791 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 1792 int match = s_urlMatcher.match(url); 1793 boolean notify = false; 1794 switch (match) 1795 { 1796 case URL_TELEPHONY_USING_SUBID: 1797 { 1798 String subIdString = url.getLastPathSegment(); 1799 try { 1800 subId = Integer.parseInt(subIdString); 1801 } catch (NumberFormatException e) { 1802 loge("NumberFormatException" + e); 1803 return result; 1804 } 1805 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 1806 } 1807 //intentional fall through from above case 1808 1809 case URL_TELEPHONY: 1810 { 1811 ContentValues values; 1812 if (initialValues != null) { 1813 values = new ContentValues(initialValues); 1814 } else { 1815 values = new ContentValues(); 1816 } 1817 1818 values = DatabaseHelper.setDefaultValue(values); 1819 if (!values.containsKey(EDITED)) { 1820 values.put(EDITED, USER_EDITED); 1821 } 1822 1823 try { 1824 // Replace on conflict so that if same APN is present in db with edited 1825 // as UNEDITED or USER/CARRIER_DELETED, it is replaced with 1826 // edited USER/CARRIER_EDITED 1827 long rowID = db.insertWithOnConflict(CARRIERS_TABLE, null, values, 1828 SQLiteDatabase.CONFLICT_REPLACE); 1829 if (rowID >= 0) { 1830 result = ContentUris.withAppendedId(CONTENT_URI, rowID); 1831 notify = true; 1832 } 1833 if (VDBG) log("insert: inserted " + values.toString() + " rowID = " + rowID); 1834 } catch (SQLException e) { 1835 log("insert: exception " + e); 1836 // Insertion failed which could be due to a conflict. Check if that is the case 1837 // and merge the entries 1838 Cursor oldRow = DatabaseHelper.selectConflictingRow(db, CARRIERS_TABLE, values); 1839 if (oldRow != null) { 1840 ContentValues mergedValues = new ContentValues(); 1841 DatabaseHelper.mergeFieldsAndUpdateDb(db, CARRIERS_TABLE, oldRow, values, 1842 mergedValues, false, getContext()); 1843 oldRow.close(); 1844 notify = true; 1845 } 1846 } 1847 1848 break; 1849 } 1850 1851 case URL_CURRENT_USING_SUBID: 1852 { 1853 String subIdString = url.getLastPathSegment(); 1854 try { 1855 subId = Integer.parseInt(subIdString); 1856 } catch (NumberFormatException e) { 1857 loge("NumberFormatException" + e); 1858 return result; 1859 } 1860 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 1861 // FIXME use subId in the query 1862 } 1863 //intentional fall through from above case 1864 1865 case URL_CURRENT: 1866 { 1867 // zero out the previous operator 1868 db.update(CARRIERS_TABLE, s_currentNullMap, CURRENT + "!=0", null); 1869 1870 String numeric = initialValues.getAsString(NUMERIC); 1871 int updated = db.update(CARRIERS_TABLE, s_currentSetMap, 1872 NUMERIC + " = '" + numeric + "'", null); 1873 1874 if (updated > 0) 1875 { 1876 if (VDBG) log("Setting numeric '" + numeric + "' to be the current operator"); 1877 } 1878 else 1879 { 1880 loge("Failed setting numeric '" + numeric + "' to the current operator"); 1881 } 1882 break; 1883 } 1884 1885 case URL_PREFERAPN_USING_SUBID: 1886 case URL_PREFERAPN_NO_UPDATE_USING_SUBID: 1887 { 1888 String subIdString = url.getLastPathSegment(); 1889 try { 1890 subId = Integer.parseInt(subIdString); 1891 } catch (NumberFormatException e) { 1892 loge("NumberFormatException" + e); 1893 return result; 1894 } 1895 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 1896 } 1897 //intentional fall through from above case 1898 1899 case URL_PREFERAPN: 1900 case URL_PREFERAPN_NO_UPDATE: 1901 { 1902 if (initialValues != null) { 1903 if(initialValues.containsKey(COLUMN_APN_ID)) { 1904 setPreferredApnId(initialValues.getAsLong(COLUMN_APN_ID), subId); 1905 } 1906 } 1907 break; 1908 } 1909 1910 case URL_SIMINFO: { 1911 long id = db.insert(SIMINFO_TABLE, null, initialValues); 1912 result = ContentUris.withAppendedId(SubscriptionManager.CONTENT_URI, id); 1913 break; 1914 } 1915 } 1916 1917 if (notify) { 1918 getContext().getContentResolver().notifyChange(CONTENT_URI, null, 1919 true, UserHandle.USER_ALL); 1920 } 1921 1922 return result; 1923 } 1924 1925 @Override delete(Uri url, String where, String[] whereArgs)1926 public synchronized int delete(Uri url, String where, String[] whereArgs) 1927 { 1928 int count = 0; 1929 int subId = SubscriptionManager.getDefaultSubscriptionId(); 1930 String userOrCarrierEdited = ") and (" + 1931 EDITED + "=" + USER_EDITED + " or " + 1932 EDITED + "=" + CARRIER_EDITED + ")"; 1933 String notUserOrCarrierEdited = ") and (" + 1934 EDITED + "!=" + USER_EDITED + " and " + 1935 EDITED + "!=" + CARRIER_EDITED + ")"; 1936 ContentValues cv = new ContentValues(); 1937 cv.put(EDITED, USER_DELETED); 1938 1939 checkPermission(); 1940 1941 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 1942 int match = s_urlMatcher.match(url); 1943 switch (match) 1944 { 1945 case URL_TELEPHONY_USING_SUBID: 1946 { 1947 String subIdString = url.getLastPathSegment(); 1948 try { 1949 subId = Integer.parseInt(subIdString); 1950 } catch (NumberFormatException e) { 1951 loge("NumberFormatException" + e); 1952 throw new IllegalArgumentException("Invalid subId " + url); 1953 } 1954 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 1955 // FIXME use subId in query 1956 } 1957 //intentional fall through from above case 1958 1959 case URL_TELEPHONY: 1960 { 1961 // Delete user/carrier edited entries 1962 count = db.delete(CARRIERS_TABLE, "(" + where + userOrCarrierEdited, whereArgs); 1963 // Otherwise mark as user deleted instead of deleting 1964 count += db.update(CARRIERS_TABLE, cv, "(" + where + notUserOrCarrierEdited, 1965 whereArgs); 1966 break; 1967 } 1968 1969 case URL_CURRENT_USING_SUBID: { 1970 String subIdString = url.getLastPathSegment(); 1971 try { 1972 subId = Integer.parseInt(subIdString); 1973 } catch (NumberFormatException e) { 1974 loge("NumberFormatException" + e); 1975 throw new IllegalArgumentException("Invalid subId " + url); 1976 } 1977 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 1978 // FIXME use subId in query 1979 } 1980 //intentional fall through from above case 1981 1982 case URL_CURRENT: 1983 { 1984 // Delete user/carrier edited entries 1985 count = db.delete(CARRIERS_TABLE, "(" + where + userOrCarrierEdited, whereArgs); 1986 // Otherwise mark as user deleted instead of deleting 1987 count += db.update(CARRIERS_TABLE, cv, "(" + where + notUserOrCarrierEdited, 1988 whereArgs); 1989 break; 1990 } 1991 1992 case URL_ID: 1993 { 1994 // Delete user/carrier edited entries 1995 count = db.delete(CARRIERS_TABLE, 1996 "(" + _ID + "=?" + userOrCarrierEdited, 1997 new String[] { url.getLastPathSegment() }); 1998 // Otherwise mark as user deleted instead of deleting 1999 count += db.update(CARRIERS_TABLE, cv, 2000 "(" + _ID + "=?" + notUserOrCarrierEdited, 2001 new String[]{url.getLastPathSegment() }); 2002 break; 2003 } 2004 2005 case URL_RESTOREAPN_USING_SUBID: { 2006 String subIdString = url.getLastPathSegment(); 2007 try { 2008 subId = Integer.parseInt(subIdString); 2009 } catch (NumberFormatException e) { 2010 loge("NumberFormatException" + e); 2011 throw new IllegalArgumentException("Invalid subId " + url); 2012 } 2013 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 2014 // FIXME use subId in query 2015 } 2016 case URL_RESTOREAPN: { 2017 count = 1; 2018 restoreDefaultAPN(subId); 2019 break; 2020 } 2021 2022 case URL_PREFERAPN_USING_SUBID: 2023 case URL_PREFERAPN_NO_UPDATE_USING_SUBID: { 2024 String subIdString = url.getLastPathSegment(); 2025 try { 2026 subId = Integer.parseInt(subIdString); 2027 } catch (NumberFormatException e) { 2028 loge("NumberFormatException" + e); 2029 throw new IllegalArgumentException("Invalid subId " + url); 2030 } 2031 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 2032 } 2033 //intentional fall through from above case 2034 2035 case URL_PREFERAPN: 2036 case URL_PREFERAPN_NO_UPDATE: 2037 { 2038 setPreferredApnId((long)INVALID_APN_ID, subId); 2039 if ((match == URL_PREFERAPN) || (match == URL_PREFERAPN_USING_SUBID)) count = 1; 2040 break; 2041 } 2042 2043 case URL_SIMINFO: { 2044 count = db.delete(SIMINFO_TABLE, where, whereArgs); 2045 break; 2046 } 2047 2048 case URL_UPDATE_DB: { 2049 updateApnDb(); 2050 count = 1; 2051 break; 2052 } 2053 2054 default: { 2055 throw new UnsupportedOperationException("Cannot delete that URL: " + url); 2056 } 2057 } 2058 2059 if (count > 0) { 2060 getContext().getContentResolver().notifyChange(CONTENT_URI, null, 2061 true, UserHandle.USER_ALL); 2062 } 2063 2064 return count; 2065 } 2066 2067 @Override update(Uri url, ContentValues values, String where, String[] whereArgs)2068 public synchronized int update(Uri url, ContentValues values, String where, String[] whereArgs) 2069 { 2070 int count = 0; 2071 int uriType = URL_UNKNOWN; 2072 int subId = SubscriptionManager.getDefaultSubscriptionId(); 2073 2074 checkPermission(); 2075 2076 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 2077 int match = s_urlMatcher.match(url); 2078 switch (match) 2079 { 2080 case URL_TELEPHONY_USING_SUBID: 2081 { 2082 String subIdString = url.getLastPathSegment(); 2083 try { 2084 subId = Integer.parseInt(subIdString); 2085 } catch (NumberFormatException e) { 2086 loge("NumberFormatException" + e); 2087 throw new IllegalArgumentException("Invalid subId " + url); 2088 } 2089 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 2090 //FIXME use subId in the query 2091 } 2092 //intentional fall through from above case 2093 2094 case URL_TELEPHONY: 2095 { 2096 if (!values.containsKey(EDITED)) { 2097 values.put(EDITED, USER_EDITED); 2098 } 2099 2100 // Replace on conflict so that if same APN is present in db with edited 2101 // as UNEDITED or USER/CARRIER_DELETED, it is replaced with 2102 // edited USER/CARRIER_EDITED 2103 count = db.updateWithOnConflict(CARRIERS_TABLE, values, where, whereArgs, 2104 SQLiteDatabase.CONFLICT_REPLACE); 2105 break; 2106 } 2107 2108 case URL_CURRENT_USING_SUBID: 2109 { 2110 String subIdString = url.getLastPathSegment(); 2111 try { 2112 subId = Integer.parseInt(subIdString); 2113 } catch (NumberFormatException e) { 2114 loge("NumberFormatException" + e); 2115 throw new IllegalArgumentException("Invalid subId " + url); 2116 } 2117 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 2118 //FIXME use subId in the query 2119 } 2120 //intentional fall through from above case 2121 2122 case URL_CURRENT: 2123 { 2124 if (!values.containsKey(EDITED)) { 2125 values.put(EDITED, USER_EDITED); 2126 } 2127 // Replace on conflict so that if same APN is present in db with edited 2128 // as UNEDITED or USER/CARRIER_DELETED, it is replaced with 2129 // edited USER/CARRIER_EDITED 2130 count = db.updateWithOnConflict(CARRIERS_TABLE, values, where, whereArgs, 2131 SQLiteDatabase.CONFLICT_REPLACE); 2132 break; 2133 } 2134 2135 case URL_ID: 2136 { 2137 if (where != null || whereArgs != null) { 2138 throw new UnsupportedOperationException( 2139 "Cannot update URL " + url + " with a where clause"); 2140 } 2141 if (!values.containsKey(EDITED)) { 2142 values.put(EDITED, USER_EDITED); 2143 } 2144 // Replace on conflict so that if same APN is present in db with edited 2145 // as UNEDITED or USER/CARRIER_DELETED, it is replaced with 2146 // edited USER/CARRIER_EDITED 2147 count = db.updateWithOnConflict(CARRIERS_TABLE, values, 2148 _ID + "=?", new String[] { url.getLastPathSegment() }, 2149 SQLiteDatabase.CONFLICT_REPLACE); 2150 break; 2151 } 2152 2153 case URL_PREFERAPN_USING_SUBID: 2154 case URL_PREFERAPN_NO_UPDATE_USING_SUBID: 2155 { 2156 String subIdString = url.getLastPathSegment(); 2157 try { 2158 subId = Integer.parseInt(subIdString); 2159 } catch (NumberFormatException e) { 2160 loge("NumberFormatException" + e); 2161 throw new IllegalArgumentException("Invalid subId " + url); 2162 } 2163 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 2164 } 2165 2166 case URL_PREFERAPN: 2167 case URL_PREFERAPN_NO_UPDATE: 2168 { 2169 if (values != null) { 2170 if (values.containsKey(COLUMN_APN_ID)) { 2171 setPreferredApnId(values.getAsLong(COLUMN_APN_ID), subId); 2172 if ((match == URL_PREFERAPN) || 2173 (match == URL_PREFERAPN_USING_SUBID)) { 2174 count = 1; 2175 } 2176 } 2177 } 2178 break; 2179 } 2180 2181 case URL_SIMINFO: { 2182 count = db.update(SIMINFO_TABLE, values, where, whereArgs); 2183 uriType = URL_SIMINFO; 2184 break; 2185 } 2186 2187 default: { 2188 throw new UnsupportedOperationException("Cannot update that URL: " + url); 2189 } 2190 } 2191 2192 if (count > 0) { 2193 switch (uriType) { 2194 case URL_SIMINFO: 2195 getContext().getContentResolver().notifyChange( 2196 SubscriptionManager.CONTENT_URI, null, true, UserHandle.USER_ALL); 2197 break; 2198 default: 2199 getContext().getContentResolver().notifyChange( 2200 CONTENT_URI, null, true, UserHandle.USER_ALL); 2201 } 2202 } 2203 2204 return count; 2205 } 2206 checkPermission()2207 private void checkPermission() { 2208 int status = getContext().checkCallingOrSelfPermission( 2209 "android.permission.WRITE_APN_SETTINGS"); 2210 if (status == PackageManager.PERMISSION_GRANTED) { 2211 return; 2212 } 2213 2214 PackageManager packageManager = getContext().getPackageManager(); 2215 String[] packages = packageManager.getPackagesForUid(Binder.getCallingUid()); 2216 2217 TelephonyManager telephonyManager = 2218 (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE); 2219 for (String pkg : packages) { 2220 if (telephonyManager.checkCarrierPrivilegesForPackage(pkg) == 2221 TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { 2222 return; 2223 } 2224 } 2225 throw new SecurityException("No permission to write APN settings"); 2226 } 2227 2228 private DatabaseHelper mOpenHelper; 2229 restoreDefaultAPN(int subId)2230 private void restoreDefaultAPN(int subId) { 2231 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 2232 2233 try { 2234 db.delete(CARRIERS_TABLE, null, null); 2235 } catch (SQLException e) { 2236 loge("got exception when deleting to restore: " + e); 2237 } 2238 setPreferredApnId((long) INVALID_APN_ID, subId); 2239 mOpenHelper.initDatabase(db); 2240 } 2241 updateApnDb()2242 private synchronized void updateApnDb() { 2243 if (!mOpenHelper.apnDbUpdateNeeded()) { 2244 log("Skipping apn db update since apn-conf has not changed."); 2245 return; 2246 } 2247 2248 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 2249 2250 // Delete preferred APN for all subIds 2251 deletePreferredApnId(); 2252 2253 // Delete entries in db 2254 try { 2255 if (VDBG) log("updateApnDb: deleting edited=UNEDITED entries"); 2256 db.delete(CARRIERS_TABLE, IS_UNEDITED, null); 2257 } catch (SQLException e) { 2258 loge("got exception when deleting to update: " + e); 2259 } 2260 2261 mOpenHelper.initDatabase(db); 2262 2263 // Notify listereners of DB change since DB has been updated 2264 getContext().getContentResolver().notifyChange( 2265 CONTENT_URI, null, true, UserHandle.USER_ALL); 2266 2267 } 2268 2269 /** 2270 * Log with debug 2271 * 2272 * @param s is string log 2273 */ log(String s)2274 private static void log(String s) { 2275 Log.d(TAG, s); 2276 } 2277 loge(String s)2278 private static void loge(String s) { 2279 Log.e(TAG, s); 2280 } 2281 } 2282