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