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