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