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 18package com.android.providers.telephony; 19 20import android.content.ContentProvider; 21import android.content.ContentUris; 22import android.content.ContentValues; 23import android.content.Context; 24import android.content.SharedPreferences; 25import android.content.UriMatcher; 26import android.content.pm.PackageManager; 27import android.content.res.Resources; 28import android.content.res.XmlResourceParser; 29import android.database.Cursor; 30import android.database.SQLException; 31import android.database.sqlite.SQLiteDatabase; 32import android.database.sqlite.SQLiteException; 33import android.database.sqlite.SQLiteOpenHelper; 34import android.database.sqlite.SQLiteQueryBuilder; 35import android.net.Uri; 36import android.os.Binder; 37import android.os.Environment; 38import android.os.FileUtils; 39import android.os.SystemProperties; 40import android.os.UserHandle; 41import android.telephony.ServiceState; 42import android.telephony.SubscriptionInfo; 43import android.telephony.SubscriptionManager; 44import android.telephony.TelephonyManager; 45import android.text.TextUtils; 46import android.util.Log; 47import android.util.Xml; 48 49import com.android.internal.util.XmlUtils; 50 51import org.xmlpull.v1.XmlPullParser; 52import org.xmlpull.v1.XmlPullParserException; 53 54import java.io.File; 55import java.io.FileNotFoundException; 56import java.io.FileReader; 57import java.io.IOException; 58import java.util.ArrayList; 59import java.util.Arrays; 60import java.util.List; 61import java.util.Map; 62 63import static android.provider.Telephony.Carriers.*; 64 65public 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); 175 s_currentNullMap.put(CURRENT, "0"); 176 177 s_currentSetMap = new ContentValues(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 */ 190 public DatabaseHelper(Context context) { 191 super(context, DATABASE_NAME, null, getVersion(context)); 192 mContext = context; 193 } 194 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 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 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 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 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 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 342 private long getApnConfChecksum() { 343 SharedPreferences sp = mContext.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); 344 return sp.getLong(APN_CONF_CHECKSUM, -1); 345 } 346 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 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 */ 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 */ 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 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 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 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 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 792 private String queryValFirst(String field) { 793 return field + "=?"; 794 } 795 796 private String queryVal(String field) { 797 return " and " + field + "=?"; 798 } 799 800 private String queryValOrNull(String field) { 801 return " and (" + field + "=? or " + field + " is null)"; 802 } 803 804 private String queryVal2OrNull(String field) { 805 return " and (" + field + "=? or " + field + "=? or " + field + " is null)"; 806 } 807 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 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 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 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 */ 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 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 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 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 */ 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 */ 2274 private static void log(String s) { 2275 Log.d(TAG, s); 2276 } 2277 2278 private static void loge(String s) { 2279 Log.e(TAG, s); 2280 } 2281} 2282