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