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.annotation.NonNull; 74 import android.annotation.Nullable; 75 import android.app.compat.CompatChanges; 76 import android.content.ComponentName; 77 import android.content.ContentProvider; 78 import android.content.ContentResolver; 79 import android.content.ContentUris; 80 import android.content.ContentValues; 81 import android.content.Context; 82 import android.content.Intent; 83 import android.content.ServiceConnection; 84 import android.content.SharedPreferences; 85 import android.content.UriMatcher; 86 import android.content.pm.PackageManager; 87 import android.content.res.Resources; 88 import android.content.res.XmlResourceParser; 89 import android.database.Cursor; 90 import android.database.MatrixCursor; 91 import android.database.SQLException; 92 import android.database.sqlite.SQLiteDatabase; 93 import android.database.sqlite.SQLiteException; 94 import android.database.sqlite.SQLiteOpenHelper; 95 import android.database.sqlite.SQLiteQueryBuilder; 96 import android.net.Uri; 97 import android.os.Binder; 98 import android.os.Bundle; 99 import android.os.Environment; 100 import android.os.IBinder; 101 import android.os.PersistableBundle; 102 import android.os.Process; 103 import android.os.RemoteException; 104 import android.os.SystemProperties; 105 import android.os.UserHandle; 106 import android.provider.Telephony; 107 import android.telephony.SubscriptionManager; 108 import android.telephony.TelephonyManager; 109 import android.telephony.TelephonyProtoEnums; 110 import android.telephony.data.ApnSetting; 111 import android.text.TextUtils; 112 import android.util.ArrayMap; 113 import android.util.ArraySet; 114 import android.util.AtomicFile; 115 import android.util.Log; 116 import android.util.Pair; 117 import android.util.Xml; 118 119 import com.android.internal.annotations.GuardedBy; 120 import com.android.internal.annotations.VisibleForTesting; 121 import com.android.internal.telephony.PhoneFactory; 122 import com.android.internal.telephony.TelephonyStatsLog; 123 import com.android.internal.util.XmlUtils; 124 import android.service.carrier.IApnSourceService; 125 126 import org.xmlpull.v1.XmlPullParser; 127 import org.xmlpull.v1.XmlPullParserException; 128 129 import java.io.ByteArrayOutputStream; 130 import java.io.File; 131 import java.io.FileInputStream; 132 import java.io.FileNotFoundException; 133 import java.io.FileOutputStream; 134 import java.io.FileReader; 135 import java.io.IOException; 136 import java.io.InputStream; 137 import java.lang.Integer; 138 import java.util.ArrayList; 139 import java.util.Arrays; 140 import java.util.concurrent.atomic.AtomicBoolean; 141 import java.util.HashMap; 142 import java.util.HashSet; 143 import java.util.List; 144 import java.util.Locale; 145 import java.util.Map; 146 import java.util.Set; 147 import java.util.stream.Collectors; 148 import java.util.zip.CheckedInputStream; 149 import java.util.zip.CRC32; 150 151 public class TelephonyProvider extends ContentProvider 152 { 153 private static final String DATABASE_NAME = "telephony.db"; 154 private static final int IDLE_CONNECTION_TIMEOUT_MS = 30000; 155 private static final boolean DBG = true; 156 private static final boolean VDBG = false; // STOPSHIP if true 157 158 private static final int DATABASE_VERSION = 51 << 16; 159 private static final int URL_UNKNOWN = 0; 160 private static final int URL_TELEPHONY = 1; 161 private static final int URL_CURRENT = 2; 162 private static final int URL_ID = 3; 163 private static final int URL_RESTOREAPN = 4; 164 private static final int URL_PREFERAPN = 5; 165 private static final int URL_PREFERAPN_NO_UPDATE = 6; 166 private static final int URL_SIMINFO = 7; 167 private static final int URL_TELEPHONY_USING_SUBID = 8; 168 private static final int URL_CURRENT_USING_SUBID = 9; 169 private static final int URL_RESTOREAPN_USING_SUBID = 10; 170 private static final int URL_PREFERAPN_USING_SUBID = 11; 171 private static final int URL_PREFERAPN_NO_UPDATE_USING_SUBID = 12; 172 private static final int URL_SIMINFO_USING_SUBID = 13; 173 private static final int URL_UPDATE_DB = 14; 174 private static final int URL_DELETE = 15; 175 private static final int URL_DPC = 16; 176 private static final int URL_DPC_ID = 17; 177 private static final int URL_FILTERED = 18; 178 private static final int URL_FILTERED_ID = 19; 179 private static final int URL_ENFORCE_MANAGED = 20; 180 // URL_PREFERAPNSET and URL_PREFERAPNSET_USING_SUBID return all APNs for the current 181 // carrier which have an apn_set_id equal to the preferred APN 182 // (if no preferred APN, or preferred APN has no set id, the query will return null) 183 private static final int URL_PREFERAPNSET = 21; 184 private static final int URL_PREFERAPNSET_USING_SUBID = 22; 185 private static final int URL_SIM_APN_LIST = 23; 186 private static final int URL_SIM_APN_LIST_ID = 24; 187 private static final int URL_FILTERED_USING_SUBID = 25; 188 private static final int URL_SIM_APN_LIST_FILTERED = 26; 189 private static final int URL_SIM_APN_LIST_FILTERED_ID = 27; 190 private static final int URL_SIMINFO_SUW_RESTORE = 28; 191 private static final int URL_SIMINFO_SIM_INSERTED_RESTORE = 29; 192 193 /** 194 * Default value for mtu if it's not set. Moved from PhoneConstants. 195 */ 196 private static final int UNSPECIFIED_INT = -1; 197 198 private static final String TAG = "TelephonyProvider"; 199 private static final String CARRIERS_TABLE = "carriers"; 200 private static final String CARRIERS_TABLE_TMP = "carriers_tmp"; 201 private static final String SIMINFO_TABLE = "siminfo"; 202 private static final String SIMINFO_TABLE_TMP = "siminfo_tmp"; 203 204 private static final String PREF_FILE_APN = "preferred-apn"; 205 private static final String COLUMN_APN_ID = "apn_id"; 206 private static final String EXPLICIT_SET_CALLED = "explicit_set_called"; 207 208 private static final String PREF_FILE_FULL_APN = "preferred-full-apn"; 209 private static final String DB_VERSION_KEY = "version"; 210 211 private static final String BUILD_ID_FILE = "build-id"; 212 private static final String RO_BUILD_ID = "ro_build_id"; 213 214 private static final String ENFORCED_FILE = "dpc-apn-enforced"; 215 private static final String ENFORCED_KEY = "enforced"; 216 217 private static final String PREF_FILE = "telephonyprovider"; 218 private static final String APN_CONF_CHECKSUM = "apn_conf_checksum"; 219 220 private static final String PARTNER_APNS_PATH = "etc/apns-conf.xml"; 221 private static final String OEM_APNS_PATH = "telephony/apns-conf.xml"; 222 private static final String OTA_UPDATED_APNS_PATH = "misc/apns/apns-conf.xml"; 223 private static final String OLD_APNS_PATH = "etc/old-apns-conf.xml"; 224 225 private static final String DEFAULT_PROTOCOL = "IP"; 226 private static final String DEFAULT_ROAMING_PROTOCOL = "IP"; 227 228 private static final UriMatcher s_urlMatcher = new UriMatcher(UriMatcher.NO_MATCH); 229 230 private static final ContentValues s_currentNullMap; 231 private static final ContentValues s_currentSetMap; 232 233 private static final String IS_UNEDITED = EDITED_STATUS + "=" + UNEDITED; 234 private static final String IS_EDITED = EDITED_STATUS + "!=" + UNEDITED; 235 private static final String IS_USER_EDITED = EDITED_STATUS + "=" + USER_EDITED; 236 private static final String IS_NOT_USER_EDITED = EDITED_STATUS + "!=" + USER_EDITED; 237 private static final String IS_USER_DELETED = EDITED_STATUS + "=" + USER_DELETED; 238 private static final String IS_NOT_USER_DELETED = EDITED_STATUS + "!=" + USER_DELETED; 239 private static final String IS_USER_DELETED_BUT_PRESENT_IN_XML = 240 EDITED_STATUS + "=" + USER_DELETED_BUT_PRESENT_IN_XML; 241 private static final String IS_NOT_USER_DELETED_BUT_PRESENT_IN_XML = 242 EDITED_STATUS + "!=" + USER_DELETED_BUT_PRESENT_IN_XML; 243 private static final String IS_CARRIER_EDITED = EDITED_STATUS + "=" + CARRIER_EDITED; 244 private static final String IS_NOT_CARRIER_EDITED = EDITED_STATUS + "!=" + CARRIER_EDITED; 245 private static final String IS_CARRIER_DELETED = EDITED_STATUS + "=" + CARRIER_DELETED; 246 private static final String IS_NOT_CARRIER_DELETED = EDITED_STATUS + "!=" + CARRIER_DELETED; 247 private static final String IS_CARRIER_DELETED_BUT_PRESENT_IN_XML = 248 EDITED_STATUS + "=" + CARRIER_DELETED_BUT_PRESENT_IN_XML; 249 private static final String IS_NOT_CARRIER_DELETED_BUT_PRESENT_IN_XML = 250 EDITED_STATUS + "!=" + CARRIER_DELETED_BUT_PRESENT_IN_XML; 251 private static final String IS_OWNED_BY_DPC = OWNED_BY + "=" + OWNED_BY_DPC; 252 private static final String IS_NOT_OWNED_BY_DPC = OWNED_BY + "!=" + OWNED_BY_DPC; 253 254 private static final String ORDER_BY_SUB_ID = 255 Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + " ASC"; 256 257 @VisibleForTesting 258 static final String BACKED_UP_SIM_SPECIFIC_SETTINGS_FILE = "sim_specific_settings_file"; 259 // Holds names and value types of SimInfoDb columns to backup. 260 private static final Map<String, Integer> SIM_INFO_COLUMNS_TO_BACKUP = new HashMap(); 261 private static final String KEY_SIMINFO_DB_ROW_PREFIX = "KEY_SIMINFO_DB_ROW_"; 262 private static final int DEFAULT_INT_COLUMN_VALUE = -111; 263 private static final String DEFAULT_STRING_COLUMN_VALUE = "DEFAULT_STRING_COLUMN_VALUE"; 264 private static final String SIM_INSERTED_RESTORE_URI_SUFFIX = "sim_inserted_restore"; 265 @VisibleForTesting 266 static final String KEY_BACKUP_DATA_FORMAT_VERSION = "KEY_BACKUP_DATA_FORMAT_VERSION"; 267 @VisibleForTesting 268 static final String KEY_PREVIOUSLY_RESTORED_SUB_IDS = "KEY_PREVIOUSLY_RESTORED_SUB_IDS"; 269 270 private static final int INVALID_APN_ID = -1; 271 private static final List<String> CARRIERS_UNIQUE_FIELDS = new ArrayList<String>(); 272 private static final Set<String> CARRIERS_BOOLEAN_FIELDS = new HashSet<String>(); 273 private static final Map<String, String> CARRIERS_UNIQUE_FIELDS_DEFAULTS = new HashMap(); 274 275 @VisibleForTesting 276 static Boolean s_apnSourceServiceExists; 277 278 protected final Object mLock = new Object(); 279 @GuardedBy("mLock") 280 private IApnSourceService mIApnSourceService; 281 private Injector mInjector; 282 283 private boolean mManagedApnEnforced; 284 285 /** 286 * Mobile country codes where there is a high likelyhood that the MNC has 3 digits 287 * and need one more prefix zero to set correct mobile network code value. 288 * 289 * Please note! The best solution is to add the MCCMNC combo to carrier id 290 * carrier_list, this is just a best effort. 291 */ 292 private static final String[] COUNTRY_MCC_WITH_THREE_DIGIT_MNC = { 293 "302" // Canada 294 ,"310" // Guam, USA 295 ,"311" // USA 296 ,"312" // USA 297 ,"313" // USA 298 ,"316" // USA 299 ,"334" // Mexico 300 ,"338" // Bermuda, Jamaica 301 ,"342" // Barbados 302 ,"344" // Antigua and Barbuda 303 ,"346" // Cayman Islands 304 ,"348" // British Virgin Islands 305 ,"356" // Saint Kitts and Nevis 306 ,"358" // Saint Lucia 307 ,"360" // Saint Vincent and the Grenadines 308 ,"365" // Anguilla 309 ,"366" // Dominica 310 ,"376" // Turks and Caicos Islands 311 ,"405" // India 312 ,"708" // Honduras 313 ,"722" // Argentina 314 ,"732" // Colombia 315 ,"738" // Guyana 316 ,"750" // Falkland Islands 317 }; 318 319 /** 320 * Available radio technologies for GSM, UMTS and CDMA. 321 * Duplicates the constants from hardware/radio/include/ril.h 322 * This should only be used by agents working with the ril. Others 323 * should use the equivalent TelephonyManager.NETWORK_TYPE_* 324 */ 325 private static final int RIL_RADIO_TECHNOLOGY_UNKNOWN = 0; 326 private static final int RIL_RADIO_TECHNOLOGY_GPRS = 1; 327 private static final int RIL_RADIO_TECHNOLOGY_EDGE = 2; 328 private static final int RIL_RADIO_TECHNOLOGY_UMTS = 3; 329 private static final int RIL_RADIO_TECHNOLOGY_IS95A = 4; 330 private static final int RIL_RADIO_TECHNOLOGY_IS95B = 5; 331 private static final int RIL_RADIO_TECHNOLOGY_1xRTT = 6; 332 private static final int RIL_RADIO_TECHNOLOGY_EVDO_0 = 7; 333 private static final int RIL_RADIO_TECHNOLOGY_EVDO_A = 8; 334 private static final int RIL_RADIO_TECHNOLOGY_HSDPA = 9; 335 private static final int RIL_RADIO_TECHNOLOGY_HSUPA = 10; 336 private static final int RIL_RADIO_TECHNOLOGY_HSPA = 11; 337 private static final int RIL_RADIO_TECHNOLOGY_EVDO_B = 12; 338 private static final int RIL_RADIO_TECHNOLOGY_EHRPD = 13; 339 private static final int RIL_RADIO_TECHNOLOGY_LTE = 14; 340 private static final int RIL_RADIO_TECHNOLOGY_HSPAP = 15; 341 342 /** 343 * GSM radio technology only supports voice. It does not support data. 344 */ 345 private static final int RIL_RADIO_TECHNOLOGY_GSM = 16; 346 private static final int RIL_RADIO_TECHNOLOGY_TD_SCDMA = 17; 347 348 /** 349 * IWLAN 350 */ 351 private static final int RIL_RADIO_TECHNOLOGY_IWLAN = 18; 352 353 /** 354 * LTE_CA 355 */ 356 private static final int RIL_RADIO_TECHNOLOGY_LTE_CA = 19; 357 358 /** 359 * NR(New Radio) 5G. 360 */ 361 private static final int RIL_RADIO_TECHNOLOGY_NR = 20; 362 363 /** 364 * The number of the radio technologies. 365 */ 366 private static final int NEXT_RIL_RADIO_TECHNOLOGY = 21; 367 368 private static final Map<String, Integer> MVNO_TYPE_STRING_MAP; 369 370 static { 371 // Columns not included in UNIQUE constraint: name, current, edited, user, server, password, 372 // authtype, type, protocol, roaming_protocol, sub_id, modem_cognitive, max_conns, 373 // wait_time, max_conns_time, mtu, bearer_bitmask, user_visible, network_type_bitmask, 374 // skip_464xlat CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(NUMERIC, "")375 CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(NUMERIC, ""); CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(MCC, "")376 CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(MCC, ""); CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(MNC, "")377 CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(MNC, ""); CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(APN, "")378 CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(APN, ""); CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(PROXY, "")379 CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(PROXY, ""); CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(PORT, "")380 CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(PORT, ""); CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(MMSPROXY, "")381 CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(MMSPROXY, ""); CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(MMSPORT, "")382 CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(MMSPORT, ""); CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(MMSC, "")383 CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(MMSC, ""); CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(CARRIER_ENABLED, "1")384 CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(CARRIER_ENABLED, "1"); CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(BEARER, "0")385 CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(BEARER, "0"); CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(MVNO_TYPE, "")386 CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(MVNO_TYPE, ""); CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(MVNO_MATCH_DATA, "")387 CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(MVNO_MATCH_DATA, ""); CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(PROFILE_ID, "0")388 CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(PROFILE_ID, "0"); CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(PROTOCOL, "IP")389 CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(PROTOCOL, "IP"); CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(ROAMING_PROTOCOL, "IP")390 CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(ROAMING_PROTOCOL, "IP"); CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(USER_EDITABLE, "1")391 CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(USER_EDITABLE, "1"); CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(OWNED_BY, String.valueOf(OWNED_BY_OTHERS))392 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))393 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))394 CARRIERS_UNIQUE_FIELDS_DEFAULTS.put(CARRIER_ID, 395 String.valueOf(TelephonyManager.UNKNOWN_CARRIER_ID)); 396 CARRIERS_UNIQUE_FIELDS_DEFAULTS.keySet()397 CARRIERS_UNIQUE_FIELDS.addAll(CARRIERS_UNIQUE_FIELDS_DEFAULTS.keySet()); 398 399 // SQLite databases store bools as ints but the ContentValues objects passed in through 400 // queries use bools. As a result there is some special handling of boolean fields within 401 // the TelephonyProvider. 402 CARRIERS_BOOLEAN_FIELDS.add(CARRIER_ENABLED); 403 CARRIERS_BOOLEAN_FIELDS.add(MODEM_PERSIST); 404 CARRIERS_BOOLEAN_FIELDS.add(USER_VISIBLE); 405 CARRIERS_BOOLEAN_FIELDS.add(USER_EDITABLE); 406 407 MVNO_TYPE_STRING_MAP = new ArrayMap<String, Integer>(); 408 MVNO_TYPE_STRING_MAP.put("spn", ApnSetting.MVNO_TYPE_SPN); 409 MVNO_TYPE_STRING_MAP.put("imsi", ApnSetting.MVNO_TYPE_IMSI); 410 MVNO_TYPE_STRING_MAP.put("gid", ApnSetting.MVNO_TYPE_GID); 411 MVNO_TYPE_STRING_MAP.put("iccid", ApnSetting.MVNO_TYPE_ICCID); 412 413 // To B&R a new config, simply add the column name and its appropriate value type to 414 // SIM_INFO_COLUMNS_TO_BACKUP. To no longer B&R a column, simply remove it from 415 // SIM_INFO_COLUMNS_TO_BACKUP. For both cases, add appropriate versioning logic in 416 // convertBackedUpDataToContentValues(ContentValues contenValues) SIM_INFO_COLUMNS_TO_BACKUP.put( Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID, Cursor.FIELD_TYPE_INTEGER)417 SIM_INFO_COLUMNS_TO_BACKUP.put( 418 Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID, Cursor.FIELD_TYPE_INTEGER); SIM_INFO_COLUMNS_TO_BACKUP.put( Telephony.SimInfo.COLUMN_ICC_ID, Cursor.FIELD_TYPE_STRING)419 SIM_INFO_COLUMNS_TO_BACKUP.put( 420 Telephony.SimInfo.COLUMN_ICC_ID, Cursor.FIELD_TYPE_STRING); SIM_INFO_COLUMNS_TO_BACKUP.put( Telephony.SimInfo.COLUMN_NUMBER, Cursor.FIELD_TYPE_STRING)421 SIM_INFO_COLUMNS_TO_BACKUP.put( 422 Telephony.SimInfo.COLUMN_NUMBER, Cursor.FIELD_TYPE_STRING); SIM_INFO_COLUMNS_TO_BACKUP.put( Telephony.SimInfo.COLUMN_CARRIER_ID, Cursor.FIELD_TYPE_INTEGER)423 SIM_INFO_COLUMNS_TO_BACKUP.put( 424 Telephony.SimInfo.COLUMN_CARRIER_ID, Cursor.FIELD_TYPE_INTEGER); SIM_INFO_COLUMNS_TO_BACKUP.put( Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED, Cursor.FIELD_TYPE_INTEGER)425 SIM_INFO_COLUMNS_TO_BACKUP.put( 426 Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED, Cursor.FIELD_TYPE_INTEGER); SIM_INFO_COLUMNS_TO_BACKUP.put( Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED, Cursor.FIELD_TYPE_INTEGER)427 SIM_INFO_COLUMNS_TO_BACKUP.put( 428 Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED, Cursor.FIELD_TYPE_INTEGER); SIM_INFO_COLUMNS_TO_BACKUP.put( Telephony.SimInfo.COLUMN_VT_IMS_ENABLED, Cursor.FIELD_TYPE_INTEGER)429 SIM_INFO_COLUMNS_TO_BACKUP.put( 430 Telephony.SimInfo.COLUMN_VT_IMS_ENABLED, Cursor.FIELD_TYPE_INTEGER); SIM_INFO_COLUMNS_TO_BACKUP.put( Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING, Cursor.FIELD_TYPE_INTEGER)431 SIM_INFO_COLUMNS_TO_BACKUP.put( 432 Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING, Cursor.FIELD_TYPE_INTEGER); SIM_INFO_COLUMNS_TO_BACKUP.put( Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED, Cursor.FIELD_TYPE_INTEGER)433 SIM_INFO_COLUMNS_TO_BACKUP.put( 434 Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED, Cursor.FIELD_TYPE_INTEGER); SIM_INFO_COLUMNS_TO_BACKUP.put( Telephony.SimInfo.COLUMN_WFC_IMS_MODE, Cursor.FIELD_TYPE_INTEGER)435 SIM_INFO_COLUMNS_TO_BACKUP.put( 436 Telephony.SimInfo.COLUMN_WFC_IMS_MODE, Cursor.FIELD_TYPE_INTEGER); SIM_INFO_COLUMNS_TO_BACKUP.put( Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE, Cursor.FIELD_TYPE_INTEGER)437 SIM_INFO_COLUMNS_TO_BACKUP.put( 438 Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE, Cursor.FIELD_TYPE_INTEGER); SIM_INFO_COLUMNS_TO_BACKUP.put( Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS, Cursor.FIELD_TYPE_STRING)439 SIM_INFO_COLUMNS_TO_BACKUP.put( 440 Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS, 441 Cursor.FIELD_TYPE_STRING); 442 443 } 444 445 @VisibleForTesting getStringForCarrierTableCreation(String tableName)446 public static String getStringForCarrierTableCreation(String tableName) { 447 return "CREATE TABLE " + tableName + 448 "(_id INTEGER PRIMARY KEY," + 449 NAME + " TEXT DEFAULT ''," + 450 NUMERIC + " TEXT DEFAULT ''," + 451 MCC + " TEXT DEFAULT ''," + 452 MNC + " TEXT DEFAULT ''," + 453 CARRIER_ID + " INTEGER DEFAULT " + TelephonyManager.UNKNOWN_CARRIER_ID + "," + 454 APN + " TEXT DEFAULT ''," + 455 USER + " TEXT DEFAULT ''," + 456 SERVER + " TEXT DEFAULT ''," + 457 PASSWORD + " TEXT DEFAULT ''," + 458 PROXY + " TEXT DEFAULT ''," + 459 PORT + " TEXT DEFAULT ''," + 460 MMSPROXY + " TEXT DEFAULT ''," + 461 MMSPORT + " TEXT DEFAULT ''," + 462 MMSC + " TEXT DEFAULT ''," + 463 AUTH_TYPE + " INTEGER DEFAULT -1," + 464 TYPE + " TEXT DEFAULT ''," + 465 CURRENT + " INTEGER," + 466 PROTOCOL + " TEXT DEFAULT " + DEFAULT_PROTOCOL + "," + 467 ROAMING_PROTOCOL + " TEXT DEFAULT " + DEFAULT_ROAMING_PROTOCOL + "," + 468 CARRIER_ENABLED + " BOOLEAN DEFAULT 1," + // SQLite databases store bools as ints 469 BEARER + " INTEGER DEFAULT 0," + 470 BEARER_BITMASK + " INTEGER DEFAULT 0," + 471 NETWORK_TYPE_BITMASK + " INTEGER DEFAULT 0," + 472 MVNO_TYPE + " TEXT DEFAULT ''," + 473 MVNO_MATCH_DATA + " TEXT DEFAULT ''," + 474 SUBSCRIPTION_ID + " INTEGER DEFAULT " + 475 SubscriptionManager.INVALID_SUBSCRIPTION_ID + "," + 476 PROFILE_ID + " INTEGER DEFAULT 0," + 477 MODEM_PERSIST + " BOOLEAN DEFAULT 0," + 478 MAX_CONNECTIONS + " INTEGER DEFAULT 0," + 479 WAIT_TIME_RETRY + " INTEGER DEFAULT 0," + 480 TIME_LIMIT_FOR_MAX_CONNECTIONS + " INTEGER DEFAULT 0," + 481 MTU + " INTEGER DEFAULT 0," + 482 EDITED_STATUS + " INTEGER DEFAULT " + UNEDITED + "," + 483 USER_VISIBLE + " BOOLEAN DEFAULT 1," + 484 USER_EDITABLE + " BOOLEAN DEFAULT 1," + 485 OWNED_BY + " INTEGER DEFAULT " + OWNED_BY_OTHERS + "," + 486 APN_SET_ID + " INTEGER DEFAULT " + NO_APN_SET_ID + "," + 487 SKIP_464XLAT + " INTEGER DEFAULT " + SKIP_464XLAT_DEFAULT + "," + 488 // Uniqueness collisions are used to trigger merge code so if a field is listed 489 // here it means we will accept both (user edited + new apn_conf definition) 490 // Columns not included in UNIQUE constraint: name, current, edited, 491 // user, server, password, authtype, type, sub_id, modem_cognitive, max_conns, 492 // wait_time, max_conns_time, mtu, bearer_bitmask, user_visible, 493 // network_type_bitmask, skip_464xlat. 494 "UNIQUE (" + TextUtils.join(", ", CARRIERS_UNIQUE_FIELDS) + "));"; 495 } 496 497 @VisibleForTesting getStringForSimInfoTableCreation(String tableName)498 public static String getStringForSimInfoTableCreation(String tableName) { 499 return "CREATE TABLE " + tableName + "(" 500 + Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID 501 + " INTEGER PRIMARY KEY AUTOINCREMENT," 502 + Telephony.SimInfo.COLUMN_ICC_ID + " TEXT NOT NULL," 503 + Telephony.SimInfo.COLUMN_SIM_SLOT_INDEX 504 + " INTEGER DEFAULT " + Telephony.SimInfo.SIM_NOT_INSERTED + "," 505 + Telephony.SimInfo.COLUMN_DISPLAY_NAME + " TEXT," 506 + Telephony.SimInfo.COLUMN_CARRIER_NAME + " TEXT," 507 + Telephony.SimInfo.COLUMN_NAME_SOURCE 508 + " INTEGER DEFAULT " + Telephony.SimInfo.NAME_SOURCE_CARRIER_ID + "," 509 + Telephony.SimInfo.COLUMN_COLOR + " INTEGER DEFAULT " 510 + Telephony.SimInfo.COLOR_DEFAULT + "," 511 + Telephony.SimInfo.COLUMN_NUMBER + " TEXT," 512 + Telephony.SimInfo.COLUMN_DISPLAY_NUMBER_FORMAT 513 + " INTEGER NOT NULL DEFAULT " + Telephony.SimInfo.DISPLAY_NUMBER_DEFAULT + "," 514 + Telephony.SimInfo.COLUMN_DATA_ROAMING 515 + " INTEGER DEFAULT " + Telephony.SimInfo.DATA_ROAMING_DISABLE + "," 516 + Telephony.SimInfo.COLUMN_MCC + " INTEGER DEFAULT 0," 517 + Telephony.SimInfo.COLUMN_MNC + " INTEGER DEFAULT 0," 518 + Telephony.SimInfo.COLUMN_MCC_STRING + " TEXT," 519 + Telephony.SimInfo.COLUMN_MNC_STRING + " TEXT," 520 + Telephony.SimInfo.COLUMN_EHPLMNS + " TEXT," 521 + Telephony.SimInfo.COLUMN_HPLMNS + " TEXT," 522 + Telephony.SimInfo.COLUMN_SIM_PROVISIONING_STATUS 523 + " INTEGER DEFAULT " + Telephony.SimInfo.SIM_PROVISIONED + "," 524 + Telephony.SimInfo.COLUMN_IS_EMBEDDED + " INTEGER DEFAULT 0," 525 + Telephony.SimInfo.COLUMN_CARD_ID + " TEXT NOT NULL," 526 + Telephony.SimInfo.COLUMN_ACCESS_RULES + " BLOB," 527 + Telephony.SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS + " BLOB," 528 + Telephony.SimInfo.COLUMN_IS_REMOVABLE + " INTEGER DEFAULT 0," 529 + Telephony.SimInfo.COLUMN_CB_EXTREME_THREAT_ALERT + " INTEGER DEFAULT 1," 530 + Telephony.SimInfo.COLUMN_CB_SEVERE_THREAT_ALERT + " INTEGER DEFAULT 1," 531 + Telephony.SimInfo.COLUMN_CB_AMBER_ALERT + " INTEGER DEFAULT 1," 532 + Telephony.SimInfo.COLUMN_CB_EMERGENCY_ALERT + " INTEGER DEFAULT 1," 533 + Telephony.SimInfo.COLUMN_CB_ALERT_SOUND_DURATION + " INTEGER DEFAULT 4," 534 + Telephony.SimInfo.COLUMN_CB_ALERT_REMINDER_INTERVAL + " INTEGER DEFAULT 0," 535 + Telephony.SimInfo.COLUMN_CB_ALERT_VIBRATE + " INTEGER DEFAULT 1," 536 + Telephony.SimInfo.COLUMN_CB_ALERT_SPEECH + " INTEGER DEFAULT 1," 537 + Telephony.SimInfo.COLUMN_CB_ETWS_TEST_ALERT + " INTEGER DEFAULT 0," 538 + Telephony.SimInfo.COLUMN_CB_CHANNEL_50_ALERT + " INTEGER DEFAULT 1," 539 + Telephony.SimInfo.COLUMN_CB_CMAS_TEST_ALERT + " INTEGER DEFAULT 0," 540 + Telephony.SimInfo.COLUMN_CB_OPT_OUT_DIALOG + " INTEGER DEFAULT 1," 541 + Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED + " INTEGER DEFAULT -1," 542 + Telephony.SimInfo.COLUMN_VT_IMS_ENABLED + " INTEGER DEFAULT -1," 543 + Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED + " INTEGER DEFAULT -1," 544 + Telephony.SimInfo.COLUMN_WFC_IMS_MODE + " INTEGER DEFAULT -1," 545 + Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE + " INTEGER DEFAULT -1," 546 + Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED + " INTEGER DEFAULT -1," 547 + Telephony.SimInfo.COLUMN_IS_OPPORTUNISTIC + " INTEGER DEFAULT 0," 548 + Telephony.SimInfo.COLUMN_GROUP_UUID + " TEXT," 549 + Telephony.SimInfo.COLUMN_IS_METERED + " INTEGER DEFAULT 1," 550 + Telephony.SimInfo.COLUMN_ISO_COUNTRY_CODE + " TEXT," 551 + Telephony.SimInfo.COLUMN_CARRIER_ID + " INTEGER DEFAULT -1," 552 + Telephony.SimInfo.COLUMN_PROFILE_CLASS + " INTEGER DEFAULT " 553 + Telephony.SimInfo.PROFILE_CLASS_UNSET + "," 554 + Telephony.SimInfo.COLUMN_SUBSCRIPTION_TYPE + " INTEGER DEFAULT " 555 + Telephony.SimInfo.SUBSCRIPTION_TYPE_LOCAL_SIM + "," 556 + Telephony.SimInfo.COLUMN_GROUP_OWNER + " TEXT," 557 + Telephony.SimInfo.COLUMN_DATA_ENABLED_OVERRIDE_RULES + " TEXT," 558 + Telephony.SimInfo.COLUMN_IMSI + " TEXT," 559 + Telephony.SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED + " INTEGER DEFAULT 1," 560 + Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES + " BIGINT DEFAULT -1," 561 + Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED + " INTEGER DEFAULT 0," 562 + Telephony.SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED + " INTEGER DEFAULT 0," 563 + Telephony.SimInfo.COLUMN_RCS_CONFIG + " BLOB," 564 + Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS + " TEXT," 565 + Telephony.SimInfo.COLUMN_VOIMS_OPT_IN_STATUS + " INTEGER DEFAULT 0," 566 + Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING + " INTEGER DEFAULT 0," 567 + Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS + " TEXT" 568 + ");"; 569 } 570 571 static { 572 s_urlMatcher.addURI("telephony", "carriers", URL_TELEPHONY); 573 s_urlMatcher.addURI("telephony", "carriers/current", URL_CURRENT); 574 s_urlMatcher.addURI("telephony", "carriers/#", URL_ID); 575 s_urlMatcher.addURI("telephony", "carriers/restore", URL_RESTOREAPN); 576 s_urlMatcher.addURI("telephony", "carriers/preferapn", URL_PREFERAPN); 577 s_urlMatcher.addURI("telephony", "carriers/preferapn_no_update", URL_PREFERAPN_NO_UPDATE); 578 s_urlMatcher.addURI("telephony", "carriers/preferapnset", URL_PREFERAPNSET); 579 580 s_urlMatcher.addURI("telephony", "siminfo", URL_SIMINFO); 581 s_urlMatcher.addURI("telephony", "siminfo/#", URL_SIMINFO_USING_SUBID); 582 s_urlMatcher.addURI("telephony", "siminfo/backup_and_restore/suw_restore", 583 URL_SIMINFO_SUW_RESTORE); 584 s_urlMatcher.addURI("telephony", "siminfo/backup_and_restore/" + 585 SIM_INSERTED_RESTORE_URI_SUFFIX, 586 URL_SIMINFO_SIM_INSERTED_RESTORE); 587 588 s_urlMatcher.addURI("telephony", "carriers/subId/*", URL_TELEPHONY_USING_SUBID); 589 s_urlMatcher.addURI("telephony", "carriers/current/subId/*", URL_CURRENT_USING_SUBID); 590 s_urlMatcher.addURI("telephony", "carriers/restore/subId/*", URL_RESTOREAPN_USING_SUBID); 591 s_urlMatcher.addURI("telephony", "carriers/preferapn/subId/*", URL_PREFERAPN_USING_SUBID); 592 s_urlMatcher.addURI("telephony", "carriers/preferapn_no_update/subId/*", 593 URL_PREFERAPN_NO_UPDATE_USING_SUBID); 594 s_urlMatcher.addURI("telephony", "carriers/preferapnset/subId/*", 595 URL_PREFERAPNSET_USING_SUBID); 596 597 s_urlMatcher.addURI("telephony", "carriers/update_db", URL_UPDATE_DB); 598 s_urlMatcher.addURI("telephony", "carriers/delete", URL_DELETE); 599 600 // Only called by DevicePolicyManager to manipulate DPC records. 601 s_urlMatcher.addURI("telephony", "carriers/dpc", URL_DPC); 602 // Only called by DevicePolicyManager to manipulate a DPC record with certain _ID. 603 s_urlMatcher.addURI("telephony", "carriers/dpc/#", URL_DPC_ID); 604 // Only called by Settings app, DcTracker and other telephony components to get APN list 605 // according to whether DPC records are enforced. 606 s_urlMatcher.addURI("telephony", "carriers/filtered", URL_FILTERED); 607 // Only called by Settings app, DcTracker and other telephony components to get a 608 // single APN according to whether DPC records are enforced. 609 s_urlMatcher.addURI("telephony", "carriers/filtered/#", URL_FILTERED_ID); 610 // Used by DcTracker to pass a subId. 611 s_urlMatcher.addURI("telephony", "carriers/filtered/subId/*", URL_FILTERED_USING_SUBID); 612 613 // Only Called by DevicePolicyManager to enforce DPC records. 614 s_urlMatcher.addURI("telephony", "carriers/enforce_managed", URL_ENFORCE_MANAGED); 615 s_urlMatcher.addURI("telephony", "carriers/sim_apn_list", URL_SIM_APN_LIST); 616 s_urlMatcher.addURI("telephony", "carriers/sim_apn_list/#", URL_SIM_APN_LIST_ID); 617 s_urlMatcher.addURI("telephony", "carriers/sim_apn_list/filtered", 618 URL_SIM_APN_LIST_FILTERED); 619 s_urlMatcher.addURI("telephony", "carriers/sim_apn_list/filtered/subId/*", 620 URL_SIM_APN_LIST_FILTERED_ID); 621 622 s_currentNullMap = new ContentValues(1); s_currentNullMap.put(CURRENT, "0")623 s_currentNullMap.put(CURRENT, "0"); 624 625 s_currentSetMap = new ContentValues(1); s_currentSetMap.put(CURRENT, "1")626 s_currentSetMap.put(CURRENT, "1"); 627 } 628 629 /** 630 * Unit test will subclass it to inject mocks. 631 */ 632 @VisibleForTesting 633 static class Injector { binderGetCallingUid()634 int binderGetCallingUid() { 635 return Binder.getCallingUid(); 636 } 637 } 638 TelephonyProvider()639 public TelephonyProvider() { 640 this(new Injector()); 641 } 642 643 @VisibleForTesting TelephonyProvider(Injector injector)644 public TelephonyProvider(Injector injector) { 645 mInjector = injector; 646 } 647 648 @VisibleForTesting getVersion(Context context)649 public static int getVersion(Context context) { 650 if (VDBG) log("getVersion:+"); 651 // Get the database version, combining a static schema version and the XML version 652 Resources r = context.getResources(); 653 if (r == null) { 654 loge("resources=null, return version=" + Integer.toHexString(DATABASE_VERSION)); 655 return DATABASE_VERSION; 656 } 657 XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns); 658 try { 659 XmlUtils.beginDocument(parser, "apns"); 660 int publicversion = Integer.parseInt(parser.getAttributeValue(null, "version")); 661 int version = DATABASE_VERSION | publicversion; 662 if (VDBG) log("getVersion:- version=0x" + Integer.toHexString(version)); 663 return version; 664 } catch (Exception e) { 665 loge("Can't get version of APN database" + e + " return version=" + 666 Integer.toHexString(DATABASE_VERSION)); 667 return DATABASE_VERSION; 668 } finally { 669 parser.close(); 670 } 671 } 672 setDefaultValue(ContentValues values)673 static public ContentValues setDefaultValue(ContentValues values) { 674 if (!values.containsKey(SUBSCRIPTION_ID)) { 675 int subId = SubscriptionManager.getDefaultSubscriptionId(); 676 values.put(SUBSCRIPTION_ID, subId); 677 } 678 679 return values; 680 } 681 682 @VisibleForTesting 683 public class DatabaseHelper extends SQLiteOpenHelper { 684 // Context to access resources with 685 private Context mContext; 686 687 /** 688 * DatabaseHelper helper class for loading apns into a database. 689 * 690 * @param context of the user. 691 */ DatabaseHelper(Context context)692 public DatabaseHelper(Context context) { 693 super(context, DATABASE_NAME, null, getVersion(context)); 694 mContext = context; 695 // Memory optimization - close idle connections after 30s of inactivity 696 setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS); 697 setWriteAheadLoggingEnabled(false); 698 } 699 700 @Override onCreate(SQLiteDatabase db)701 public void onCreate(SQLiteDatabase db) { 702 if (DBG) log("dbh.onCreate:+ db=" + db); 703 createSimInfoTable(db, SIMINFO_TABLE); 704 createCarriersTable(db, CARRIERS_TABLE); 705 // if CarrierSettings app is installed, we expect it to do the initializiation instead 706 if (apnSourceServiceExists(mContext)) { 707 log("dbh.onCreate: Skipping apply APNs from xml."); 708 } else { 709 log("dbh.onCreate: Apply apns from xml."); 710 initDatabase(db); 711 } 712 if (DBG) log("dbh.onCreate:- db=" + db); 713 } 714 715 @Override onOpen(SQLiteDatabase db)716 public void onOpen(SQLiteDatabase db) { 717 if (VDBG) log("dbh.onOpen:+ db=" + db); 718 try { 719 // Try to access the table and create it if "no such table" 720 db.query(SIMINFO_TABLE, null, null, null, null, null, null); 721 if (DBG) log("dbh.onOpen: ok, queried table=" + SIMINFO_TABLE); 722 } catch (SQLiteException e) { 723 loge("Exception " + SIMINFO_TABLE + "e=" + e); 724 if (e.getMessage().startsWith("no such table")) { 725 createSimInfoTable(db, SIMINFO_TABLE); 726 } 727 } 728 try { 729 db.query(CARRIERS_TABLE, null, null, null, null, null, null); 730 if (DBG) log("dbh.onOpen: ok, queried table=" + CARRIERS_TABLE); 731 } catch (SQLiteException e) { 732 loge("Exception " + CARRIERS_TABLE + " e=" + e); 733 if (e.getMessage().startsWith("no such table")) { 734 createCarriersTable(db, CARRIERS_TABLE); 735 } 736 } 737 if (VDBG) log("dbh.onOpen:- db=" + db); 738 } 739 createSimInfoTable(SQLiteDatabase db, String tableName)740 private void createSimInfoTable(SQLiteDatabase db, String tableName) { 741 if (DBG) log("dbh.createSimInfoTable:+ " + tableName); 742 db.execSQL(getStringForSimInfoTableCreation(tableName)); 743 if (DBG) log("dbh.createSimInfoTable:-"); 744 } 745 createCarriersTable(SQLiteDatabase db, String tableName)746 private void createCarriersTable(SQLiteDatabase db, String tableName) { 747 // Set up the database schema 748 if (DBG) log("dbh.createCarriersTable: " + tableName); 749 db.execSQL(getStringForCarrierTableCreation(tableName)); 750 if (DBG) log("dbh.createCarriersTable:-"); 751 } 752 getChecksum(File file)753 private long getChecksum(File file) { 754 CRC32 checkSummer = new CRC32(); 755 long checkSum = -1; 756 try (CheckedInputStream cis = 757 new CheckedInputStream(new FileInputStream(file), checkSummer)){ 758 byte[] buf = new byte[128]; 759 if(cis != null) { 760 while(cis.read(buf) >= 0) { 761 // Just read for checksum to get calculated. 762 } 763 } 764 checkSum = checkSummer.getValue(); 765 if (DBG) log("Checksum for " + file.getAbsolutePath() + " is " + checkSum); 766 } catch (FileNotFoundException e) { 767 loge("FileNotFoundException for " + file.getAbsolutePath() + ":" + e); 768 } catch (IOException e) { 769 loge("IOException for " + file.getAbsolutePath() + ":" + e); 770 } 771 772 // The RRO may have been updated in a firmware upgrade. Add checksum for the 773 // resources to the total checksum so that apns in an RRO update is not missed. 774 try (InputStream inputStream = mContext.getResources(). 775 openRawResource(com.android.internal.R.xml.apns)) { 776 byte[] array = toByteArray(inputStream); 777 checkSummer.reset(); 778 checkSummer.update(array); 779 checkSum += checkSummer.getValue(); 780 if (DBG) log("Checksum after adding resource is " + checkSummer.getValue()); 781 } catch (IOException | Resources.NotFoundException e) { 782 loge("Exception when calculating checksum for internal apn resources: " + e); 783 } 784 return checkSum; 785 } 786 toByteArray(InputStream input)787 private byte[] toByteArray(InputStream input) throws IOException { 788 byte[] buffer = new byte[128]; 789 int bytesRead; 790 ByteArrayOutputStream output = new ByteArrayOutputStream(); 791 while ((bytesRead = input.read(buffer)) != -1) { 792 output.write(buffer, 0, bytesRead); 793 } 794 return output.toByteArray(); 795 } 796 getApnConfChecksum()797 private long getApnConfChecksum() { 798 SharedPreferences sp = mContext.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); 799 return sp.getLong(APN_CONF_CHECKSUM, -1); 800 } 801 setApnConfChecksum(long checksum)802 private void setApnConfChecksum(long checksum) { 803 SharedPreferences sp = mContext.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); 804 SharedPreferences.Editor editor = sp.edit(); 805 editor.putLong(APN_CONF_CHECKSUM, checksum); 806 editor.apply(); 807 } 808 getApnConfFile()809 private File getApnConfFile() { 810 // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system". 811 File confFile = new File(Environment.getRootDirectory(), PARTNER_APNS_PATH); 812 File oemConfFile = new File(Environment.getOemDirectory(), OEM_APNS_PATH); 813 File updatedConfFile = new File(Environment.getDataDirectory(), OTA_UPDATED_APNS_PATH); 814 File productConfFile = new File(Environment.getProductDirectory(), PARTNER_APNS_PATH); 815 confFile = pickSecondIfExists(confFile, oemConfFile); 816 confFile = pickSecondIfExists(confFile, productConfFile); 817 confFile = pickSecondIfExists(confFile, updatedConfFile); 818 return confFile; 819 } 820 821 /** 822 * This function computes checksum for the file to be read and compares it against the 823 * last read file. DB needs to be updated only if checksum has changed, or old checksum does 824 * not exist. 825 * @return true if DB should be updated with new conf file, false otherwise 826 */ apnDbUpdateNeeded()827 private boolean apnDbUpdateNeeded() { 828 File confFile = getApnConfFile(); 829 long newChecksum = getChecksum(confFile); 830 long oldChecksum = getApnConfChecksum(); 831 if (DBG) log("newChecksum: " + newChecksum); 832 if (DBG) log("oldChecksum: " + oldChecksum); 833 if (newChecksum == oldChecksum) { 834 return false; 835 } else { 836 return true; 837 } 838 } 839 840 /** 841 * This function adds APNs from xml file(s) to db. The db may or may not be empty to begin 842 * with. 843 */ initDatabase(SQLiteDatabase db)844 private void initDatabase(SQLiteDatabase db) { 845 if (VDBG) log("dbh.initDatabase:+ db=" + db); 846 // Read internal APNS data 847 Resources r = mContext.getResources(); 848 int publicversion = -1; 849 if (r != null) { 850 XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns); 851 try { 852 XmlUtils.beginDocument(parser, "apns"); 853 publicversion = Integer.parseInt(parser.getAttributeValue(null, "version")); 854 loadApns(db, parser, true); 855 } catch (Exception e) { 856 loge("Got exception while loading APN database." + e); 857 } finally { 858 parser.close(); 859 } 860 } else { 861 loge("initDatabase: resources=null"); 862 } 863 864 // Read external APNS data (partner-provided) 865 XmlPullParser confparser = null; 866 File confFile = getApnConfFile(); 867 868 FileReader confreader = null; 869 if (DBG) log("confFile = " + confFile); 870 try { 871 confreader = new FileReader(confFile); 872 confparser = Xml.newPullParser(); 873 confparser.setInput(confreader); 874 XmlUtils.beginDocument(confparser, "apns"); 875 876 // Correctness check. Force internal version and confidential versions to agree 877 int confversion = Integer.parseInt(confparser.getAttributeValue(null, "version")); 878 if (publicversion != confversion) { 879 log("initDatabase: throwing exception due to version mismatch"); 880 throw new IllegalStateException("Internal APNS file version doesn't match " 881 + confFile.getAbsolutePath()); 882 } 883 884 loadApns(db, confparser, false); 885 } catch (FileNotFoundException e) { 886 // It's ok if the file isn't found. It means there isn't a confidential file 887 // Log.e(TAG, "File not found: '" + confFile.getAbsolutePath() + "'"); 888 } catch (Exception e) { 889 loge("initDatabase: Exception while parsing '" + confFile.getAbsolutePath() + "'" + 890 e); 891 } finally { 892 // Get rid of user/carrier deleted entries that are not present in apn xml file. 893 // Those entries have edited value USER_DELETED/CARRIER_DELETED. 894 if (VDBG) { 895 log("initDatabase: deleting USER_DELETED and replacing " 896 + "DELETED_BUT_PRESENT_IN_XML with DELETED"); 897 } 898 899 // Delete USER_DELETED 900 db.delete(CARRIERS_TABLE, IS_USER_DELETED + " or " + IS_CARRIER_DELETED, null); 901 902 // Change USER_DELETED_BUT_PRESENT_IN_XML to USER_DELETED 903 ContentValues cv = new ContentValues(); 904 cv.put(EDITED_STATUS, USER_DELETED); 905 db.update(CARRIERS_TABLE, cv, IS_USER_DELETED_BUT_PRESENT_IN_XML, null); 906 907 // Change CARRIER_DELETED_BUT_PRESENT_IN_XML to CARRIER_DELETED 908 cv = new ContentValues(); 909 cv.put(EDITED_STATUS, CARRIER_DELETED); 910 db.update(CARRIERS_TABLE, cv, IS_CARRIER_DELETED_BUT_PRESENT_IN_XML, null); 911 912 if (confreader != null) { 913 try { 914 confreader.close(); 915 } catch (IOException e) { 916 // do nothing 917 } 918 } 919 920 // Update the stored checksum 921 setApnConfChecksum(getChecksum(confFile)); 922 } 923 if (VDBG) log("dbh.initDatabase:- db=" + db); 924 925 } 926 pickSecondIfExists(File sysApnFile, File altApnFile)927 private File pickSecondIfExists(File sysApnFile, File altApnFile) { 928 if (altApnFile.exists()) { 929 if (DBG) log("Load APNs from " + altApnFile.getPath() + 930 " instead of " + sysApnFile.getPath()); 931 return altApnFile; 932 } else { 933 if (DBG) log("Load APNs from " + sysApnFile.getPath() + 934 " instead of " + altApnFile.getPath()); 935 return sysApnFile; 936 } 937 } 938 939 @Override onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)940 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 941 if (DBG) { 942 log("dbh.onUpgrade:+ db=" + db + " oldV=" + oldVersion + " newV=" + newVersion); 943 } 944 945 deletePreferredApnId(mContext); 946 947 if (oldVersion < (5 << 16 | 6)) { 948 // 5 << 16 is the Database version and 6 in the xml version. 949 950 // This change adds a new authtype column to the database. 951 // The auth type column can have 4 values: 0 (None), 1 (PAP), 2 (CHAP) 952 // 3 (PAP or CHAP). To avoid breaking compatibility, with already working 953 // APNs, the unset value (-1) will be used. If the value is -1. 954 // the authentication will default to 0 (if no user / password) is specified 955 // or to 3. Currently, there have been no reported problems with 956 // pre-configured APNs and hence it is set to -1 for them. Similarly, 957 // if the user, has added a new APN, we set the authentication type 958 // to -1. 959 960 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 961 " ADD COLUMN authtype INTEGER DEFAULT -1;"); 962 963 oldVersion = 5 << 16 | 6; 964 } 965 if (oldVersion < (6 << 16 | 6)) { 966 // Add protcol fields to the APN. The XML file does not change. 967 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 968 " ADD COLUMN protocol TEXT DEFAULT IP;"); 969 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 970 " ADD COLUMN roaming_protocol TEXT DEFAULT IP;"); 971 oldVersion = 6 << 16 | 6; 972 } 973 if (oldVersion < (7 << 16 | 6)) { 974 // Add carrier_enabled, bearer fields to the APN. The XML file does not change. 975 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 976 " ADD COLUMN carrier_enabled BOOLEAN DEFAULT 1;"); 977 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 978 " ADD COLUMN bearer INTEGER DEFAULT 0;"); 979 oldVersion = 7 << 16 | 6; 980 } 981 if (oldVersion < (8 << 16 | 6)) { 982 // Add mvno_type, mvno_match_data fields to the APN. 983 // The XML file does not change. 984 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 985 " ADD COLUMN mvno_type TEXT DEFAULT '';"); 986 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 987 " ADD COLUMN mvno_match_data TEXT DEFAULT '';"); 988 oldVersion = 8 << 16 | 6; 989 } 990 if (oldVersion < (9 << 16 | 6)) { 991 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 992 " ADD COLUMN sub_id INTEGER DEFAULT " + 993 SubscriptionManager.INVALID_SUBSCRIPTION_ID + ";"); 994 oldVersion = 9 << 16 | 6; 995 } 996 if (oldVersion < (10 << 16 | 6)) { 997 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 998 " ADD COLUMN profile_id INTEGER DEFAULT 0;"); 999 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 1000 " ADD COLUMN modem_cognitive BOOLEAN DEFAULT 0;"); 1001 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 1002 " ADD COLUMN max_conns INTEGER DEFAULT 0;"); 1003 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 1004 " ADD COLUMN wait_time INTEGER DEFAULT 0;"); 1005 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 1006 " ADD COLUMN max_conns_time INTEGER DEFAULT 0;"); 1007 oldVersion = 10 << 16 | 6; 1008 } 1009 if (oldVersion < (11 << 16 | 6)) { 1010 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 1011 " ADD COLUMN mtu INTEGER DEFAULT 0;"); 1012 oldVersion = 11 << 16 | 6; 1013 } 1014 if (oldVersion < (12 << 16 | 6)) { 1015 try { 1016 // Try to update the siminfo table. It might not be there. 1017 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + 1018 " ADD COLUMN " + Telephony.SimInfo.COLUMN_MCC + " INTEGER DEFAULT 0;"); 1019 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + 1020 " ADD COLUMN " + Telephony.SimInfo.COLUMN_MNC + " INTEGER DEFAULT 0;"); 1021 } catch (SQLiteException e) { 1022 if (DBG) { 1023 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1024 " The table will get created in onOpen."); 1025 } 1026 } 1027 oldVersion = 12 << 16 | 6; 1028 } 1029 if (oldVersion < (13 << 16 | 6)) { 1030 try { 1031 // Try to update the siminfo table. It might not be there. 1032 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " + 1033 Telephony.SimInfo.COLUMN_CARRIER_NAME + " TEXT DEFAULT '';"); 1034 } catch (SQLiteException e) { 1035 if (DBG) { 1036 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1037 " The table will get created in onOpen."); 1038 } 1039 } 1040 oldVersion = 13 << 16 | 6; 1041 } 1042 if (oldVersion < (14 << 16 | 6)) { 1043 // Do nothing. This is to avoid recreating table twice. Table is anyway recreated 1044 // for next version and that takes care of updates for this version as well. 1045 // This version added a new column user_edited to carriers db. 1046 } 1047 if (oldVersion < (15 << 16 | 6)) { 1048 // Most devices should be upgrading from version 13. On upgrade new db will be 1049 // populated from the xml included in OTA but user and carrier edited/added entries 1050 // need to be preserved. This new version also adds new columns EDITED and 1051 // BEARER_BITMASK to the table. Upgrade steps from version 13 are: 1052 // 1. preserve user and carrier added/edited APNs (by comparing against 1053 // old-apns-conf.xml included in OTA) - done in preserveUserAndCarrierApns() 1054 // 2. add new columns EDITED and BEARER_BITMASK (create a new table for that) - done 1055 // in createCarriersTable() 1056 // 3. copy over preserved APNs from old table to new table - done in 1057 // copyPreservedApnsToNewTable() 1058 // The only exception if upgrading from version 14 is that EDITED field is already 1059 // present (but is called USER_EDITED) 1060 /********************************************************************************* 1061 * IMPORTANT NOTE: SINCE CARRIERS TABLE IS RECREATED HERE, IT WILL BE THE LATEST 1062 * VERSION AFTER THIS. AS A RESULT ANY SUBSEQUENT UPDATES TO THE TABLE WILL FAIL 1063 * (DUE TO COLUMN-ALREADY-EXISTS KIND OF EXCEPTION). ALL SUBSEQUENT UPDATES SHOULD 1064 * HANDLE THAT GRACEFULLY. 1065 *********************************************************************************/ 1066 Cursor c; 1067 String[] proj = {"_id"}; 1068 if (VDBG) { 1069 c = db.query(CARRIERS_TABLE, proj, null, null, null, null, null); 1070 log("dbh.onUpgrade:- before upgrading total number of rows: " + c.getCount()); 1071 } 1072 1073 // Compare db with old apns xml file so that any user or carrier edited/added 1074 // entries can be preserved across upgrade 1075 preserveUserAndCarrierApns(db); 1076 1077 c = db.query(CARRIERS_TABLE, null, null, null, null, null, null); 1078 1079 if (VDBG) { 1080 log("dbh.onUpgrade:- after preserveUserAndCarrierApns() total number of " + 1081 "rows: " + ((c == null) ? 0 : c.getCount())); 1082 } 1083 1084 createCarriersTable(db, CARRIERS_TABLE_TMP); 1085 1086 copyPreservedApnsToNewTable(db, c); 1087 c.close(); 1088 1089 db.execSQL("DROP TABLE IF EXISTS " + CARRIERS_TABLE); 1090 1091 db.execSQL("ALTER TABLE " + CARRIERS_TABLE_TMP + " rename to " + CARRIERS_TABLE + 1092 ";"); 1093 1094 if (VDBG) { 1095 c = db.query(CARRIERS_TABLE, proj, null, null, null, null, null); 1096 log("dbh.onUpgrade:- after upgrading total number of rows: " + c.getCount()); 1097 c.close(); 1098 c = db.query(CARRIERS_TABLE, proj, IS_UNEDITED, null, null, null, null); 1099 log("dbh.onUpgrade:- after upgrading total number of rows with " + IS_UNEDITED + 1100 ": " + c.getCount()); 1101 c.close(); 1102 c = db.query(CARRIERS_TABLE, proj, IS_EDITED, null, null, null, null); 1103 log("dbh.onUpgrade:- after upgrading total number of rows with " + IS_EDITED + 1104 ": " + c.getCount()); 1105 c.close(); 1106 } 1107 1108 oldVersion = 15 << 16 | 6; 1109 } 1110 if (oldVersion < (16 << 16 | 6)) { 1111 try { 1112 // Try to update the siminfo table. It might not be there. 1113 // These columns may already be present in which case execSQL will throw an 1114 // exception 1115 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1116 + Telephony.SimInfo.COLUMN_CB_EXTREME_THREAT_ALERT 1117 + " INTEGER DEFAULT 1;"); 1118 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1119 + Telephony.SimInfo.COLUMN_CB_SEVERE_THREAT_ALERT 1120 + " INTEGER DEFAULT 1;"); 1121 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1122 + Telephony.SimInfo.COLUMN_CB_AMBER_ALERT + " INTEGER DEFAULT 1;"); 1123 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1124 + Telephony.SimInfo.COLUMN_CB_EMERGENCY_ALERT + " INTEGER DEFAULT 1;"); 1125 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1126 + Telephony.SimInfo.COLUMN_CB_ALERT_SOUND_DURATION 1127 + " INTEGER DEFAULT 4;"); 1128 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1129 + Telephony.SimInfo.COLUMN_CB_ALERT_REMINDER_INTERVAL 1130 + " INTEGER DEFAULT 0;"); 1131 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1132 + Telephony.SimInfo.COLUMN_CB_ALERT_VIBRATE + " INTEGER DEFAULT 1;"); 1133 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1134 + Telephony.SimInfo.COLUMN_CB_ALERT_SPEECH + " INTEGER DEFAULT 1;"); 1135 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1136 + Telephony.SimInfo.COLUMN_CB_ETWS_TEST_ALERT + " INTEGER DEFAULT 0;"); 1137 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1138 + Telephony.SimInfo.COLUMN_CB_CHANNEL_50_ALERT + " INTEGER DEFAULT 1;"); 1139 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1140 + Telephony.SimInfo.COLUMN_CB_CMAS_TEST_ALERT + " INTEGER DEFAULT 0;"); 1141 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1142 + Telephony.SimInfo.COLUMN_CB_OPT_OUT_DIALOG + " INTEGER DEFAULT 1;"); 1143 } catch (SQLiteException e) { 1144 if (DBG) { 1145 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1146 " The table will get created in onOpen."); 1147 } 1148 } 1149 oldVersion = 16 << 16 | 6; 1150 } 1151 if (oldVersion < (17 << 16 | 6)) { 1152 Cursor c = null; 1153 try { 1154 c = db.query(CARRIERS_TABLE, null, null, null, null, null, null, 1155 String.valueOf(1)); 1156 if (c == null || c.getColumnIndex(USER_VISIBLE) == -1) { 1157 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + " ADD COLUMN " + 1158 USER_VISIBLE + " BOOLEAN DEFAULT 1;"); 1159 } else { 1160 if (DBG) { 1161 log("onUpgrade skipping " + CARRIERS_TABLE + " upgrade. Column " + 1162 USER_VISIBLE + " already exists."); 1163 } 1164 } 1165 } finally { 1166 if (c != null) { 1167 c.close(); 1168 } 1169 } 1170 oldVersion = 17 << 16 | 6; 1171 } 1172 if (oldVersion < (18 << 16 | 6)) { 1173 try { 1174 // Try to update the siminfo table. It might not be there. 1175 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " + 1176 Telephony.SimInfo.COLUMN_SIM_PROVISIONING_STATUS + " INTEGER DEFAULT " + 1177 Telephony.SimInfo.SIM_PROVISIONED + ";"); 1178 } catch (SQLiteException e) { 1179 if (DBG) { 1180 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1181 " The table will get created in onOpen."); 1182 } 1183 } 1184 oldVersion = 18 << 16 | 6; 1185 } 1186 if (oldVersion < (19 << 16 | 6)) { 1187 // Do nothing. This is to avoid recreating table twice. Table is anyway recreated 1188 // for version 24 and that takes care of updates for this version as well. 1189 // This version added more fields protocol and roaming protocol to the primary key. 1190 } 1191 if (oldVersion < (20 << 16 | 6)) { 1192 try { 1193 // Try to update the siminfo table. It might not be there. 1194 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " + 1195 Telephony.SimInfo.COLUMN_IS_EMBEDDED + " INTEGER DEFAULT 0;"); 1196 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " + 1197 Telephony.SimInfo.COLUMN_ACCESS_RULES + " BLOB;"); 1198 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " + 1199 Telephony.SimInfo.COLUMN_IS_REMOVABLE + " INTEGER DEFAULT 0;"); 1200 } catch (SQLiteException e) { 1201 if (DBG) { 1202 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1203 "The table will get created in onOpen."); 1204 } 1205 } 1206 oldVersion = 20 << 16 | 6; 1207 } 1208 if (oldVersion < (21 << 16 | 6)) { 1209 try { 1210 // Try to update the carriers table. It might not be there. 1211 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + " ADD COLUMN " + 1212 USER_EDITABLE + " INTEGER DEFAULT 1;"); 1213 } catch (SQLiteException e) { 1214 // This is possible if the column already exists which may be the case if the 1215 // table was just created as part of upgrade to version 19 1216 if (DBG) { 1217 log("onUpgrade skipping " + CARRIERS_TABLE + " upgrade. " + 1218 "The table will get created in onOpen."); 1219 } 1220 } 1221 oldVersion = 21 << 16 | 6; 1222 } 1223 if (oldVersion < (22 << 16 | 6)) { 1224 try { 1225 // Try to update the siminfo table. It might not be there. 1226 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1227 + Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED 1228 + " INTEGER DEFAULT -1;"); 1229 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1230 + Telephony.SimInfo.COLUMN_VT_IMS_ENABLED + " INTEGER DEFAULT -1;"); 1231 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1232 + Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED + " INTEGER DEFAULT -1;"); 1233 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1234 + Telephony.SimInfo.COLUMN_WFC_IMS_MODE + " INTEGER DEFAULT -1;"); 1235 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1236 + Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE + " INTEGER DEFAULT -1;"); 1237 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1238 + Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED + " INTEGER DEFAULT -1;"); 1239 } catch (SQLiteException e) { 1240 if (DBG) { 1241 log("onUpgrade skipping " + CARRIERS_TABLE + " upgrade. " + 1242 "The table will get created in onOpen."); 1243 } 1244 } 1245 oldVersion = 22 << 16 | 6; 1246 } 1247 if (oldVersion < (23 << 16 | 6)) { 1248 try { 1249 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + " ADD COLUMN " + 1250 OWNED_BY + " INTEGER DEFAULT " + OWNED_BY_OTHERS + ";"); 1251 } catch (SQLiteException e) { 1252 if (DBG) { 1253 log("onUpgrade skipping " + CARRIERS_TABLE + " upgrade. " + 1254 "The table will get created in onOpen."); 1255 } 1256 } 1257 oldVersion = 23 << 16 | 6; 1258 } 1259 if (oldVersion < (24 << 16 | 6)) { 1260 Cursor c = null; 1261 String[] proj = {"_id"}; 1262 recreateDB(db, proj, /* version */24); 1263 if (VDBG) { 1264 c = db.query(CARRIERS_TABLE, proj, null, null, null, null, null); 1265 log("dbh.onUpgrade:- after upgrading total number of rows: " + c.getCount()); 1266 c.close(); 1267 c = db.query( 1268 CARRIERS_TABLE, proj, NETWORK_TYPE_BITMASK, null, null, null, null); 1269 log("dbh.onUpgrade:- after upgrading total number of rows with " 1270 + NETWORK_TYPE_BITMASK + ": " + c.getCount()); 1271 c.close(); 1272 } 1273 oldVersion = 24 << 16 | 6; 1274 } 1275 if (oldVersion < (25 << 16 | 6)) { 1276 // Add a new column SubscriptionManager.CARD_ID into the database and set the value 1277 // to be the same as the existing column SubscriptionManager.ICC_ID. In order to do 1278 // this, we need to first make a copy of the existing SIMINFO_TABLE, set the value 1279 // of the new column SubscriptionManager.CARD_ID, and replace the SIMINFO_TABLE with 1280 // the new table. 1281 Cursor c = null; 1282 String[] proj = {Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID}; 1283 recreateSimInfoDB(c, db, proj); 1284 if (VDBG) { 1285 c = db.query(SIMINFO_TABLE, proj, null, null, null, null, null); 1286 log("dbh.onUpgrade:- after upgrading " + SIMINFO_TABLE 1287 + " total number of rows: " + c.getCount()); 1288 c.close(); 1289 c = db.query(SIMINFO_TABLE, proj, Telephony.SimInfo.COLUMN_CARD_ID 1290 + " IS NOT NULL", null, null, null, null); 1291 log("dbh.onUpgrade:- after upgrading total number of rows with " 1292 + Telephony.SimInfo.COLUMN_CARD_ID + ": " + c.getCount()); 1293 c.close(); 1294 } 1295 oldVersion = 25 << 16 | 6; 1296 } 1297 if (oldVersion < (26 << 16 | 6)) { 1298 // Add a new column Carriers.APN_SET_ID into the database and set the value to 1299 // Carriers.NO_SET_SET by default. 1300 try { 1301 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + " ADD COLUMN " + 1302 APN_SET_ID + " INTEGER DEFAULT " + NO_APN_SET_ID + ";"); 1303 } catch (SQLiteException e) { 1304 if (DBG) { 1305 log("onUpgrade skipping " + CARRIERS_TABLE + " upgrade. " + 1306 "The table will get created in onOpen."); 1307 } 1308 } 1309 oldVersion = 26 << 16 | 6; 1310 } 1311 1312 if (oldVersion < (27 << 16 | 6)) { 1313 // Add the new MCC_STRING and MNC_STRING columns into the subscription table, 1314 // and attempt to populate them. 1315 try { 1316 // Try to update the siminfo table. It might not be there. 1317 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + 1318 " ADD COLUMN " + Telephony.SimInfo.COLUMN_MCC_STRING + " TEXT;"); 1319 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + 1320 " ADD COLUMN " + Telephony.SimInfo.COLUMN_MNC_STRING + " TEXT;"); 1321 } catch (SQLiteException e) { 1322 if (DBG) { 1323 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1324 " The table will get created in onOpen."); 1325 } 1326 } 1327 // Migrate the old integer values over to strings 1328 String[] proj = {Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID, 1329 Telephony.SimInfo.COLUMN_MCC, Telephony.SimInfo.COLUMN_MNC}; 1330 try (Cursor c = db.query(SIMINFO_TABLE, proj, null, null, null, null, null)) { 1331 while (c.moveToNext()) { 1332 fillInMccMncStringAtCursor(mContext, db, c); 1333 } 1334 } 1335 oldVersion = 27 << 16 | 6; 1336 } 1337 1338 if (oldVersion < (28 << 16 | 6)) { 1339 try { 1340 // Try to update the siminfo table. It might not be there. 1341 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1342 + Telephony.SimInfo.COLUMN_IS_OPPORTUNISTIC + " INTEGER DEFAULT 0;"); 1343 } catch (SQLiteException e) { 1344 if (DBG) { 1345 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1346 "The table will get created in onOpen."); 1347 } 1348 } 1349 oldVersion = 28 << 16 | 6; 1350 } 1351 1352 if (oldVersion < (29 << 16 | 6)) { 1353 try { 1354 // Add a new column Telephony.CARRIER_ID into the database and add UNIQUE 1355 // constraint into table. However, sqlite cannot add constraints to an existing 1356 // table, so recreate the table. 1357 String[] proj = {"_id"}; 1358 recreateDB(db, proj, /* version */29); 1359 } catch (SQLiteException e) { 1360 if (DBG) { 1361 log("onUpgrade skipping " + CARRIERS_TABLE + " upgrade. " + 1362 "The table will get created in onOpen."); 1363 } 1364 } 1365 oldVersion = 29 << 16 | 6; 1366 } 1367 1368 if (oldVersion < (30 << 16 | 6)) { 1369 try { 1370 // Try to update the siminfo table. It might not be there. 1371 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1372 + Telephony.SimInfo.COLUMN_GROUP_UUID + " TEXT;"); 1373 } catch (SQLiteException e) { 1374 if (DBG) { 1375 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1376 "The table will get created in onOpen."); 1377 } 1378 } 1379 oldVersion = 30 << 16 | 6; 1380 } 1381 1382 if (oldVersion < (31 << 16 | 6)) { 1383 try { 1384 // Try to update the siminfo table. It might not be there. 1385 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1386 + Telephony.SimInfo.COLUMN_IS_METERED + " INTEGER DEFAULT 1;"); 1387 } catch (SQLiteException e) { 1388 if (DBG) { 1389 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1390 "The table will get created in onOpen."); 1391 } 1392 } 1393 oldVersion = 31 << 16 | 6; 1394 } 1395 1396 if (oldVersion < (32 << 16 | 6)) { 1397 try { 1398 // Try to update the siminfo table. It might not be there. 1399 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1400 + Telephony.SimInfo.COLUMN_ISO_COUNTRY_CODE + " TEXT;"); 1401 } catch (SQLiteException e) { 1402 if (DBG) { 1403 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1404 "The table will get created in onOpen."); 1405 } 1406 } 1407 oldVersion = 32 << 16 | 6; 1408 } 1409 1410 if (oldVersion < (33 << 16 | 6)) { 1411 try { 1412 // Try to update the siminfo table. It might not be there. 1413 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1414 + Telephony.SimInfo.COLUMN_CARRIER_ID + " INTEGER DEFAULT -1;"); 1415 } catch (SQLiteException e) { 1416 if (DBG) { 1417 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1418 "The table will get created in onOpen."); 1419 } 1420 } 1421 oldVersion = 33 << 16 | 6; 1422 } 1423 1424 if (oldVersion < (34 << 16 | 6)) { 1425 try { 1426 // Try to update the siminfo table. It might not be there. 1427 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " + 1428 Telephony.SimInfo.COLUMN_PROFILE_CLASS + " INTEGER DEFAULT " + 1429 Telephony.SimInfo.PROFILE_CLASS_UNSET + ";"); 1430 } catch (SQLiteException e) { 1431 if (DBG) { 1432 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1433 "The table will get created in onOpen."); 1434 } 1435 } 1436 oldVersion = 34 << 16 | 6; 1437 } 1438 1439 if (oldVersion < (35 << 16 | 6)) { 1440 try { 1441 // Try to update the siminfo table. It might not be there. 1442 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1443 + Telephony.SimInfo.COLUMN_SUBSCRIPTION_TYPE + " INTEGER DEFAULT " 1444 + Telephony.SimInfo.SUBSCRIPTION_TYPE_LOCAL_SIM + ";"); 1445 } catch (SQLiteException e) { 1446 if (DBG) { 1447 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1448 "The table will get created in onOpen."); 1449 } 1450 } 1451 oldVersion = 35 << 16 | 6; 1452 } 1453 1454 if (oldVersion < (36 << 16 | 6)) { 1455 // Add a new column Carriers.SKIP_464XLAT into the database and set the value to 1456 // SKIP_464XLAT_DEFAULT. 1457 try { 1458 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + " ADD COLUMN " + 1459 SKIP_464XLAT + " INTEGER DEFAULT " + SKIP_464XLAT_DEFAULT + ";"); 1460 } catch (SQLiteException e) { 1461 if (DBG) { 1462 log("onUpgrade skipping " + CARRIERS_TABLE + " upgrade. " + 1463 "The table will get created in onOpen."); 1464 } 1465 } 1466 oldVersion = 36 << 16 | 6; 1467 } 1468 1469 if (oldVersion < (37 << 16 | 6)) { 1470 // Add new columns Telephony.SimInfo.EHPLMNS and Telephony.SimInfo.HPLMNS into 1471 // the database. 1472 try { 1473 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + 1474 " ADD COLUMN " + Telephony.SimInfo.COLUMN_EHPLMNS + " TEXT;"); 1475 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + 1476 " ADD COLUMN " + Telephony.SimInfo.COLUMN_HPLMNS + " TEXT;"); 1477 } catch (SQLiteException e) { 1478 if (DBG) { 1479 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade for ehplmns. " + 1480 "The table will get created in onOpen."); 1481 } 1482 } 1483 oldVersion = 37 << 16 | 6; 1484 } 1485 1486 if (oldVersion < (39 << 16 | 6)) { 1487 try { 1488 // Try to update the siminfo table. It might not be there. 1489 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1490 + Telephony.SimInfo.COLUMN_GROUP_OWNER + " TEXT;"); 1491 } catch (SQLiteException e) { 1492 if (DBG) { 1493 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1494 "The table will get created in onOpen."); 1495 } 1496 } 1497 oldVersion = 39 << 16 | 6; 1498 } 1499 1500 if (oldVersion < (40 << 16 | 6)) { 1501 try { 1502 // Try to update the siminfo table. It might not be there. 1503 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1504 + Telephony.SimInfo.COLUMN_DATA_ENABLED_OVERRIDE_RULES + " TEXT;"); 1505 } catch (SQLiteException e) { 1506 if (DBG) { 1507 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1508 "The table will get created in onOpen."); 1509 } 1510 } 1511 oldVersion = 40 << 16 | 6; 1512 } 1513 1514 if (oldVersion < (41 << 16 | 6)) { 1515 try { 1516 // Try to update the siminfo table. It might not be there. 1517 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1518 + Telephony.SimInfo.COLUMN_IMSI + " TEXT;"); 1519 } catch (SQLiteException e) { 1520 if (DBG) { 1521 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1522 "The table will get created in onOpen."); 1523 } 1524 } 1525 oldVersion = 41 << 16 | 6; 1526 } 1527 1528 if (oldVersion < (42 << 16 | 6)) { 1529 try { 1530 // Try to update the siminfo table. It might not be there. 1531 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " + 1532 Telephony.SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS + " BLOB;"); 1533 } catch (SQLiteException e) { 1534 if (DBG) { 1535 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1536 "The table will get created in onOpen."); 1537 } 1538 } 1539 } 1540 1541 if (oldVersion < (43 << 16 | 6)) { 1542 try { 1543 // Try to update the siminfo table. It might not be there. 1544 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1545 + Telephony.SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED 1546 + " INTEGER DEFAULT 1;"); 1547 } catch (SQLiteException e) { 1548 if (DBG) { 1549 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1550 "The table will get created in onOpen."); 1551 } 1552 } 1553 oldVersion = 43 << 16 | 6; 1554 } 1555 1556 if (oldVersion < (44 << 16 | 6)) { 1557 try { 1558 // Try to update the siminfo table. It might not be there. 1559 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1560 + Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES 1561 + " BIGINT DEFAULT -1;"); 1562 } catch (SQLiteException e) { 1563 if (DBG) { 1564 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1565 "The table will get created in onOpen."); 1566 } 1567 } 1568 oldVersion = 44 << 16 | 6; 1569 } 1570 1571 if (oldVersion < (45 << 16 | 6)) { 1572 try { 1573 // Try to update the siminfo table. It might not be there. 1574 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1575 + Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED 1576 + " INTEGER DEFAULT 0;"); 1577 } catch (SQLiteException e) { 1578 if (DBG) { 1579 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1580 "The table will get created in onOpen."); 1581 } 1582 } 1583 oldVersion = 45 << 16 | 6; 1584 } 1585 1586 if (oldVersion < (46 << 16 | 6)) { 1587 try { 1588 // Try to update the siminfo table. It might not be there. 1589 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1590 + Telephony.SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED 1591 + " INTEGER DEFAULT 0;"); 1592 } catch (SQLiteException e) { 1593 if (DBG) { 1594 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1595 "The table will get created in onOpen."); 1596 } 1597 } 1598 oldVersion = 46 << 16 | 6; 1599 } 1600 1601 if (oldVersion < (47 << 16 | 6)) { 1602 try { 1603 // Try to update the siminfo table. It might not be there. 1604 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1605 + Telephony.SimInfo.COLUMN_RCS_CONFIG 1606 + " BLOB;"); 1607 } catch (SQLiteException e) { 1608 if (DBG) { 1609 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1610 "The table will get created in onOpen."); 1611 } 1612 } 1613 oldVersion = 47 << 16 | 6; 1614 } 1615 1616 if (oldVersion < (48 << 16 | 6)) { 1617 try { 1618 // Try to update the siminfo table. It might not be there. 1619 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1620 + Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS 1621 + " TEXT;"); 1622 } catch (SQLiteException e) { 1623 if (DBG) { 1624 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " + 1625 "The table will get created in onOpen."); 1626 } 1627 } 1628 try { 1629 // Migrate the old Long values over to String 1630 String[] proj = {Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID, 1631 Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES}; 1632 try (Cursor c = db.query(SIMINFO_TABLE, proj, null, null, null, null, null)) { 1633 while (c.moveToNext()) { 1634 fillInAllowedNetworkTypesStringAtCursor(db, c); 1635 } 1636 } 1637 1638 } catch (SQLiteException e) { 1639 if (DBG) { 1640 log("can't migrate value from COLUMN_ALLOWED_NETWORK_TYPES to " 1641 + "COLUMN_ALLOWED_NETWORK_TYPES_ALL_REASON"); 1642 } 1643 } 1644 oldVersion = 48 << 16 | 6; 1645 } 1646 1647 if (oldVersion < (49 << 16 | 6)) { 1648 try { 1649 // Try to update the siminfo table. It might not be there. 1650 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1651 + Telephony.SimInfo.COLUMN_VOIMS_OPT_IN_STATUS 1652 + " INTEGER DEFAULT 0;"); 1653 } catch (SQLiteException e) { 1654 if (DBG) { 1655 log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " 1656 + "The table will get created in onOpen."); 1657 } 1658 } 1659 oldVersion = 49 << 16 | 6; 1660 } 1661 1662 if (oldVersion < (50 << 16 | 6)) { 1663 try { 1664 // Try to update the siminfo table. It might not be there. 1665 db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1666 + Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING 1667 + " INTEGER DEFAULT 0;"); 1668 } catch (SQLiteException e) { 1669 if (DBG) { 1670 log("onUpgrade failed to updated " + SIMINFO_TABLE 1671 + " to add d2d status sharing column. "); 1672 } 1673 } 1674 oldVersion = 50 << 16 | 6; 1675 } 1676 1677 if (oldVersion < (51 << 16 | 6)) { 1678 try { 1679 // Try to update the siminfo table. It might not be there. 1680 db.execSQL("ALERT TABLE " + SIMINFO_TABLE + " ADD COLUMN " 1681 + Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS 1682 + " TEXT;"); 1683 } catch (SQLiteException e) { 1684 if (DBG) { 1685 log("onUpgrade failed to updated " + SIMINFO_TABLE 1686 + " to add d2d status sharing contacts. "); 1687 } 1688 } 1689 oldVersion = 51 << 16 | 6; 1690 } 1691 if (DBG) { 1692 log("dbh.onUpgrade:- db=" + db + " oldV=" + oldVersion + " newV=" + newVersion); 1693 } 1694 // when adding fields to onUpgrade, also add a unit test to TelephonyDatabaseHelperTest 1695 // and update the DATABASE_VERSION field and add a column in copyAllApnValues 1696 } 1697 recreateSimInfoDB(Cursor c, SQLiteDatabase db, String[] proj)1698 private void recreateSimInfoDB(Cursor c, SQLiteDatabase db, String[] proj) { 1699 if (VDBG) { 1700 c = db.query(SIMINFO_TABLE, proj, null, null, null, null, null); 1701 log("dbh.onUpgrade:+ before upgrading " + SIMINFO_TABLE + 1702 " total number of rows: " + c.getCount()); 1703 c.close(); 1704 } 1705 1706 // Sort in ascending order by subscription id to make sure the rows do not get flipped 1707 // during the query and added in the new sim info table in another order (sub id is 1708 // stored in settings between migrations). 1709 c = db.query(SIMINFO_TABLE, null, null, null, null, null, ORDER_BY_SUB_ID); 1710 1711 db.execSQL("DROP TABLE IF EXISTS " + SIMINFO_TABLE_TMP); 1712 1713 createSimInfoTable(db, SIMINFO_TABLE_TMP); 1714 1715 copySimInfoDataToTmpTable(db, c); 1716 c.close(); 1717 1718 db.execSQL("DROP TABLE IF EXISTS " + SIMINFO_TABLE); 1719 1720 db.execSQL("ALTER TABLE " + SIMINFO_TABLE_TMP + " rename to " + SIMINFO_TABLE + ";"); 1721 1722 } 1723 copySimInfoDataToTmpTable(SQLiteDatabase db, Cursor c)1724 private void copySimInfoDataToTmpTable(SQLiteDatabase db, Cursor c) { 1725 // Move entries from SIMINFO_TABLE to SIMINFO_TABLE_TMP 1726 if (c != null) { 1727 while (c.moveToNext()) { 1728 ContentValues cv = new ContentValues(); 1729 copySimInfoValuesV24(cv, c); 1730 // The card ID is supposed to be the ICCID of the profile for UICC card, and 1731 // the EID of the card for eUICC card. Since EID is unknown for old entries in 1732 // SIMINFO_TABLE, we use ICCID as the card ID for all the old entries while 1733 // upgrading the SIMINFO_TABLE. In UiccController, both the card ID and ICCID 1734 // will be checked when user queries the slot information using the card ID 1735 // from the database. 1736 getCardIdfromIccid(cv, c); 1737 try { 1738 db.insert(SIMINFO_TABLE_TMP, null, cv); 1739 if (VDBG) { 1740 log("dbh.copySimInfoDataToTmpTable: db.insert returned >= 0; " + 1741 "insert successful for cv " + cv); 1742 } 1743 } catch (SQLException e) { 1744 if (VDBG) 1745 log("dbh.copySimInfoDataToTmpTable insertWithOnConflict exception " + 1746 e + " for cv " + cv); 1747 } 1748 } 1749 } 1750 } 1751 copySimInfoValuesV24(ContentValues cv, Cursor c)1752 private void copySimInfoValuesV24(ContentValues cv, Cursor c) { 1753 // String vals 1754 getStringValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_ICC_ID); 1755 getStringValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_DISPLAY_NAME); 1756 getStringValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_CARRIER_NAME); 1757 getStringValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_NUMBER); 1758 1759 // bool/int vals 1760 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_SIM_SLOT_INDEX); 1761 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_NAME_SOURCE); 1762 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_COLOR); 1763 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_DISPLAY_NUMBER_FORMAT); 1764 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_DATA_ROAMING); 1765 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_MCC); 1766 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_MNC); 1767 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_SIM_PROVISIONING_STATUS); 1768 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_IS_EMBEDDED); 1769 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_IS_REMOVABLE); 1770 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_CB_EXTREME_THREAT_ALERT); 1771 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_CB_SEVERE_THREAT_ALERT); 1772 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_CB_AMBER_ALERT); 1773 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_CB_EMERGENCY_ALERT); 1774 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_CB_ALERT_SOUND_DURATION); 1775 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_CB_ALERT_REMINDER_INTERVAL); 1776 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_CB_ALERT_VIBRATE); 1777 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_CB_ALERT_SPEECH); 1778 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_CB_ETWS_TEST_ALERT); 1779 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_CB_CHANNEL_50_ALERT); 1780 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_CB_CMAS_TEST_ALERT); 1781 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_CB_OPT_OUT_DIALOG); 1782 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED); 1783 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_VT_IMS_ENABLED); 1784 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED); 1785 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_WFC_IMS_MODE); 1786 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE); 1787 getIntValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED); 1788 1789 // Blob vals 1790 getBlobValueFromCursor(cv, c, Telephony.SimInfo.COLUMN_ACCESS_RULES); 1791 } 1792 getCardIdfromIccid(ContentValues cv, Cursor c)1793 private void getCardIdfromIccid(ContentValues cv, Cursor c) { 1794 int columnIndex = c.getColumnIndex(Telephony.SimInfo.COLUMN_ICC_ID); 1795 if (columnIndex != -1) { 1796 String fromCursor = c.getString(columnIndex); 1797 if (!TextUtils.isEmpty(fromCursor)) { 1798 cv.put(Telephony.SimInfo.COLUMN_CARD_ID, fromCursor); 1799 } 1800 } 1801 } 1802 recreateDB(SQLiteDatabase db, String[] proj, int version)1803 private void recreateDB(SQLiteDatabase db, String[] proj, int version) { 1804 // Upgrade steps are: 1805 // 1. Create a temp table- done in createCarriersTable() 1806 // 2. copy over APNs from old table to new table - done in copyDataToTmpTable() 1807 // 3. Drop the existing table. 1808 // 4. Copy over the tmp table. 1809 Cursor c; 1810 if (VDBG) { 1811 c = db.query(CARRIERS_TABLE, proj, null, null, null, null, null); 1812 log("dbh.onUpgrade:- before upgrading total number of rows: " + c.getCount()); 1813 c.close(); 1814 } 1815 1816 c = db.query(CARRIERS_TABLE, null, null, null, null, null, null); 1817 1818 if (VDBG) { 1819 log("dbh.onUpgrade:- starting data copy of existing rows: " + 1820 + ((c == null) ? 0 : c.getCount())); 1821 } 1822 1823 db.execSQL("DROP TABLE IF EXISTS " + CARRIERS_TABLE_TMP); 1824 1825 createCarriersTable(db, CARRIERS_TABLE_TMP); 1826 1827 copyDataToTmpTable(db, c, version); 1828 c.close(); 1829 1830 db.execSQL("DROP TABLE IF EXISTS " + CARRIERS_TABLE); 1831 1832 db.execSQL("ALTER TABLE " + CARRIERS_TABLE_TMP + " rename to " + CARRIERS_TABLE + ";"); 1833 } 1834 preserveUserAndCarrierApns(SQLiteDatabase db)1835 private void preserveUserAndCarrierApns(SQLiteDatabase db) { 1836 if (VDBG) log("preserveUserAndCarrierApns"); 1837 XmlPullParser confparser; 1838 File confFile = new File(Environment.getRootDirectory(), OLD_APNS_PATH); 1839 FileReader confreader = null; 1840 try { 1841 confreader = new FileReader(confFile); 1842 confparser = Xml.newPullParser(); 1843 confparser.setInput(confreader); 1844 XmlUtils.beginDocument(confparser, "apns"); 1845 1846 deleteMatchingApns(db, confparser); 1847 } catch (FileNotFoundException e) { 1848 // This function is called only when upgrading db to version 15. Details about the 1849 // upgrade are mentioned in onUpgrade(). This file missing means user/carrier added 1850 // APNs cannot be preserved. Log an error message so that OEMs know they need to 1851 // include old apns file for comparison. 1852 loge("PRESERVEUSERANDCARRIERAPNS: " + OLD_APNS_PATH + 1853 " NOT FOUND. IT IS NEEDED TO UPGRADE FROM OLDER VERSIONS OF APN " + 1854 "DB WHILE PRESERVING USER/CARRIER ADDED/EDITED ENTRIES."); 1855 } catch (Exception e) { 1856 loge("preserveUserAndCarrierApns: Exception while parsing '" + 1857 confFile.getAbsolutePath() + "'" + e); 1858 } finally { 1859 if (confreader != null) { 1860 try { 1861 confreader.close(); 1862 } catch (IOException e) { 1863 // do nothing 1864 } 1865 } 1866 } 1867 } 1868 deleteMatchingApns(SQLiteDatabase db, XmlPullParser parser)1869 private void deleteMatchingApns(SQLiteDatabase db, XmlPullParser parser) { 1870 if (VDBG) log("deleteMatchingApns"); 1871 if (parser != null) { 1872 if (VDBG) log("deleteMatchingApns: parser != null"); 1873 try { 1874 XmlUtils.nextElement(parser); 1875 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 1876 ContentValues row = getRow(parser, false); 1877 if (row == null) { 1878 throw new XmlPullParserException("Expected 'apn' tag", parser, null); 1879 } 1880 deleteRow(db, row); 1881 XmlUtils.nextElement(parser); 1882 } 1883 } catch (XmlPullParserException e) { 1884 loge("deleteMatchingApns: Got XmlPullParserException while deleting apns." + e); 1885 } catch (IOException e) { 1886 loge("deleteMatchingApns: Got IOException while deleting apns." + e); 1887 } catch (SQLException e) { 1888 loge("deleteMatchingApns: Got SQLException while deleting apns." + e); 1889 } 1890 } 1891 } 1892 queryValFirst(String field)1893 private String queryValFirst(String field) { 1894 return field + "=?"; 1895 } 1896 queryVal(String field)1897 private String queryVal(String field) { 1898 return " and " + field + "=?"; 1899 } 1900 queryValOrNull(String field)1901 private String queryValOrNull(String field) { 1902 return " and (" + field + "=? or " + field + " is null)"; 1903 } 1904 queryVal2OrNull(String field)1905 private String queryVal2OrNull(String field) { 1906 return " and (" + field + "=? or " + field + "=? or " + field + " is null)"; 1907 } 1908 deleteRow(SQLiteDatabase db, ContentValues values)1909 private void deleteRow(SQLiteDatabase db, ContentValues values) { 1910 if (VDBG) log("deleteRow"); 1911 String where = queryValFirst(NUMERIC) + 1912 queryVal(MNC) + 1913 queryVal(MNC) + 1914 queryValOrNull(APN) + 1915 queryValOrNull(USER) + 1916 queryValOrNull(SERVER) + 1917 queryValOrNull(PASSWORD) + 1918 queryValOrNull(PROXY) + 1919 queryValOrNull(PORT) + 1920 queryValOrNull(MMSPROXY) + 1921 queryValOrNull(MMSPORT) + 1922 queryValOrNull(MMSC) + 1923 queryValOrNull(AUTH_TYPE) + 1924 queryValOrNull(TYPE) + 1925 queryValOrNull(PROTOCOL) + 1926 queryValOrNull(ROAMING_PROTOCOL) + 1927 queryVal2OrNull(CARRIER_ENABLED) + 1928 queryValOrNull(BEARER) + 1929 queryValOrNull(MVNO_TYPE) + 1930 queryValOrNull(MVNO_MATCH_DATA) + 1931 queryValOrNull(PROFILE_ID) + 1932 queryVal2OrNull(MODEM_PERSIST) + 1933 queryValOrNull(MAX_CONNECTIONS) + 1934 queryValOrNull(WAIT_TIME_RETRY) + 1935 queryValOrNull(TIME_LIMIT_FOR_MAX_CONNECTIONS) + 1936 queryValOrNull(MTU); 1937 String[] whereArgs = new String[29]; 1938 int i = 0; 1939 whereArgs[i++] = values.getAsString(NUMERIC); 1940 whereArgs[i++] = values.getAsString(MCC); 1941 whereArgs[i++] = values.getAsString(MNC); 1942 whereArgs[i++] = values.getAsString(NAME); 1943 whereArgs[i++] = values.containsKey(APN) ? 1944 values.getAsString(APN) : ""; 1945 whereArgs[i++] = values.containsKey(USER) ? 1946 values.getAsString(USER) : ""; 1947 whereArgs[i++] = values.containsKey(SERVER) ? 1948 values.getAsString(SERVER) : ""; 1949 whereArgs[i++] = values.containsKey(PASSWORD) ? 1950 values.getAsString(PASSWORD) : ""; 1951 whereArgs[i++] = values.containsKey(PROXY) ? 1952 values.getAsString(PROXY) : ""; 1953 whereArgs[i++] = values.containsKey(PORT) ? 1954 values.getAsString(PORT) : ""; 1955 whereArgs[i++] = values.containsKey(MMSPROXY) ? 1956 values.getAsString(MMSPROXY) : ""; 1957 whereArgs[i++] = values.containsKey(MMSPORT) ? 1958 values.getAsString(MMSPORT) : ""; 1959 whereArgs[i++] = values.containsKey(MMSC) ? 1960 values.getAsString(MMSC) : ""; 1961 whereArgs[i++] = values.containsKey(AUTH_TYPE) ? 1962 values.getAsString(AUTH_TYPE) : "-1"; 1963 whereArgs[i++] = values.containsKey(TYPE) ? 1964 values.getAsString(TYPE) : ""; 1965 whereArgs[i++] = values.containsKey(PROTOCOL) ? 1966 values.getAsString(PROTOCOL) : DEFAULT_PROTOCOL; 1967 whereArgs[i++] = values.containsKey(ROAMING_PROTOCOL) ? 1968 values.getAsString(ROAMING_PROTOCOL) : DEFAULT_ROAMING_PROTOCOL; 1969 1970 if (values.containsKey(CARRIER_ENABLED)) { 1971 whereArgs[i++] = convertStringToBoolString(values.getAsString(CARRIER_ENABLED)); 1972 whereArgs[i++] = convertStringToIntString(values.getAsString(CARRIER_ENABLED)); 1973 } else { 1974 String defaultIntString = CARRIERS_UNIQUE_FIELDS_DEFAULTS.get(CARRIER_ENABLED); 1975 whereArgs[i++] = convertStringToBoolString(defaultIntString); 1976 whereArgs[i++] = defaultIntString; 1977 } 1978 1979 whereArgs[i++] = values.containsKey(BEARER) ? 1980 values.getAsString(BEARER) : "0"; 1981 whereArgs[i++] = values.containsKey(MVNO_TYPE) ? 1982 values.getAsString(MVNO_TYPE) : ""; 1983 whereArgs[i++] = values.containsKey(MVNO_MATCH_DATA) ? 1984 values.getAsString(MVNO_MATCH_DATA) : ""; 1985 whereArgs[i++] = values.containsKey(PROFILE_ID) ? 1986 values.getAsString(PROFILE_ID) : "0"; 1987 1988 if (values.containsKey(MODEM_PERSIST) && 1989 (values.getAsString(MODEM_PERSIST). 1990 equalsIgnoreCase("true") || 1991 values.getAsString(MODEM_PERSIST).equals("1"))) { 1992 whereArgs[i++] = "true"; 1993 whereArgs[i++] = "1"; 1994 } else { 1995 whereArgs[i++] = "false"; 1996 whereArgs[i++] = "0"; 1997 } 1998 1999 whereArgs[i++] = values.containsKey(MAX_CONNECTIONS) ? 2000 values.getAsString(MAX_CONNECTIONS) : "0"; 2001 whereArgs[i++] = values.containsKey(WAIT_TIME_RETRY) ? 2002 values.getAsString(WAIT_TIME_RETRY) : "0"; 2003 whereArgs[i++] = values.containsKey(TIME_LIMIT_FOR_MAX_CONNECTIONS) ? 2004 values.getAsString(TIME_LIMIT_FOR_MAX_CONNECTIONS) : "0"; 2005 whereArgs[i++] = values.containsKey(MTU) ? 2006 values.getAsString(MTU) : "0"; 2007 2008 if (VDBG) { 2009 log("deleteRow: where: " + where); 2010 2011 StringBuilder builder = new StringBuilder(); 2012 for (String s : whereArgs) { 2013 builder.append(s + ", "); 2014 } 2015 2016 log("deleteRow: whereArgs: " + builder.toString()); 2017 } 2018 db.delete(CARRIERS_TABLE, where, whereArgs); 2019 } 2020 copyDataToTmpTable(SQLiteDatabase db, Cursor c, int version)2021 private void copyDataToTmpTable(SQLiteDatabase db, Cursor c, int version) { 2022 // Move entries from CARRIERS_TABLE to CARRIERS_TABLE_TMP 2023 if (c != null) { 2024 while (c.moveToNext()) { 2025 ContentValues cv = new ContentValues(); 2026 copyAllApnValues(cv, c); 2027 if (version == 24) { 2028 // Sync bearer bitmask and network type bitmask 2029 getNetworkTypeBitmaskFromCursor(cv, c); 2030 } 2031 try { 2032 db.insertWithOnConflict(CARRIERS_TABLE_TMP, null, cv, 2033 SQLiteDatabase.CONFLICT_ABORT); 2034 if (VDBG) { 2035 log("dbh.copyPreservedApnsToNewTable: db.insert returned >= 0; " + 2036 "insert successful for cv " + cv); 2037 } 2038 } catch (SQLException e) { 2039 if (VDBG) 2040 log("dbh.copyPreservedApnsToNewTable insertWithOnConflict exception " + 2041 e + " for cv " + cv); 2042 } 2043 } 2044 } 2045 } 2046 copyApnValuesV17(ContentValues cv, Cursor c)2047 private void copyApnValuesV17(ContentValues cv, Cursor c) { 2048 // Include only non-null values in cv so that null values can be replaced 2049 // with default if there's a default value for the field 2050 2051 // String vals 2052 getStringValueFromCursor(cv, c, NAME); 2053 getStringValueFromCursor(cv, c, NUMERIC); 2054 getStringValueFromCursor(cv, c, MCC); 2055 getStringValueFromCursor(cv, c, MNC); 2056 getStringValueFromCursor(cv, c, APN); 2057 getStringValueFromCursor(cv, c, USER); 2058 getStringValueFromCursor(cv, c, SERVER); 2059 getStringValueFromCursor(cv, c, PASSWORD); 2060 getStringValueFromCursor(cv, c, PROXY); 2061 getStringValueFromCursor(cv, c, PORT); 2062 getStringValueFromCursor(cv, c, MMSPROXY); 2063 getStringValueFromCursor(cv, c, MMSPORT); 2064 getStringValueFromCursor(cv, c, MMSC); 2065 getStringValueFromCursor(cv, c, TYPE); 2066 getStringValueFromCursor(cv, c, PROTOCOL); 2067 getStringValueFromCursor(cv, c, ROAMING_PROTOCOL); 2068 getStringValueFromCursor(cv, c, MVNO_TYPE); 2069 getStringValueFromCursor(cv, c, MVNO_MATCH_DATA); 2070 2071 // bool/int vals 2072 getIntValueFromCursor(cv, c, AUTH_TYPE); 2073 getIntValueFromCursor(cv, c, CURRENT); 2074 getIntValueFromCursor(cv, c, CARRIER_ENABLED); 2075 getIntValueFromCursor(cv, c, BEARER); 2076 getIntValueFromCursor(cv, c, SUBSCRIPTION_ID); 2077 getIntValueFromCursor(cv, c, PROFILE_ID); 2078 getIntValueFromCursor(cv, c, MODEM_PERSIST); 2079 getIntValueFromCursor(cv, c, MAX_CONNECTIONS); 2080 getIntValueFromCursor(cv, c, WAIT_TIME_RETRY); 2081 getIntValueFromCursor(cv, c, TIME_LIMIT_FOR_MAX_CONNECTIONS); 2082 getIntValueFromCursor(cv, c, MTU); 2083 getIntValueFromCursor(cv, c, BEARER_BITMASK); 2084 getIntValueFromCursor(cv, c, EDITED_STATUS); 2085 getIntValueFromCursor(cv, c, USER_VISIBLE); 2086 } 2087 copyAllApnValues(ContentValues cv, Cursor c)2088 private void copyAllApnValues(ContentValues cv, Cursor c) { 2089 // String vals 2090 getStringValueFromCursor(cv, c, NAME); 2091 getStringValueFromCursor(cv, c, NUMERIC); 2092 getStringValueFromCursor(cv, c, MCC); 2093 getStringValueFromCursor(cv, c, MNC); 2094 getStringValueFromCursor(cv, c, APN); 2095 getStringValueFromCursor(cv, c, USER); 2096 getStringValueFromCursor(cv, c, SERVER); 2097 getStringValueFromCursor(cv, c, PASSWORD); 2098 getStringValueFromCursor(cv, c, PROXY); 2099 getStringValueFromCursor(cv, c, PORT); 2100 getStringValueFromCursor(cv, c, MMSPROXY); 2101 getStringValueFromCursor(cv, c, MMSPORT); 2102 getStringValueFromCursor(cv, c, MMSC); 2103 getStringValueFromCursor(cv, c, TYPE); 2104 getStringValueFromCursor(cv, c, PROTOCOL); 2105 getStringValueFromCursor(cv, c, ROAMING_PROTOCOL); 2106 getStringValueFromCursor(cv, c, MVNO_TYPE); 2107 getStringValueFromCursor(cv, c, MVNO_MATCH_DATA); 2108 2109 // bool/int vals 2110 getIntValueFromCursor(cv, c, AUTH_TYPE); 2111 getIntValueFromCursor(cv, c, CURRENT); 2112 getIntValueFromCursor(cv, c, CARRIER_ENABLED); 2113 getIntValueFromCursor(cv, c, BEARER); 2114 getIntValueFromCursor(cv, c, SUBSCRIPTION_ID); 2115 getIntValueFromCursor(cv, c, PROFILE_ID); 2116 getIntValueFromCursor(cv, c, MODEM_PERSIST); 2117 getIntValueFromCursor(cv, c, MAX_CONNECTIONS); 2118 getIntValueFromCursor(cv, c, WAIT_TIME_RETRY); 2119 getIntValueFromCursor(cv, c, TIME_LIMIT_FOR_MAX_CONNECTIONS); 2120 getIntValueFromCursor(cv, c, MTU); 2121 getIntValueFromCursor(cv, c, NETWORK_TYPE_BITMASK); 2122 getIntValueFromCursor(cv, c, BEARER_BITMASK); 2123 getIntValueFromCursor(cv, c, EDITED_STATUS); 2124 getIntValueFromCursor(cv, c, USER_VISIBLE); 2125 getIntValueFromCursor(cv, c, USER_EDITABLE); 2126 getIntValueFromCursor(cv, c, OWNED_BY); 2127 getIntValueFromCursor(cv, c, APN_SET_ID); 2128 getIntValueFromCursor(cv, c, SKIP_464XLAT); 2129 } 2130 copyPreservedApnsToNewTable(SQLiteDatabase db, Cursor c)2131 private void copyPreservedApnsToNewTable(SQLiteDatabase db, Cursor c) { 2132 // Move entries from CARRIERS_TABLE to CARRIERS_TABLE_TMP 2133 if (c != null && mContext.getResources() != null) { 2134 try { 2135 String[] persistApnsForPlmns = mContext.getResources().getStringArray( 2136 R.array.persist_apns_for_plmn); 2137 while (c.moveToNext()) { 2138 ContentValues cv = new ContentValues(); 2139 String val; 2140 // Using V17 copy function for V15 upgrade. This should be fine since it handles 2141 // columns that may not exist properly (getStringValueFromCursor() and 2142 // getIntValueFromCursor() handle column index -1) 2143 copyApnValuesV17(cv, c); 2144 // Change bearer to a bitmask 2145 String bearerStr = c.getString(c.getColumnIndex(BEARER)); 2146 if (!TextUtils.isEmpty(bearerStr)) { 2147 int bearer_bitmask = getBitmaskForTech(Integer.parseInt(bearerStr)); 2148 cv.put(BEARER_BITMASK, bearer_bitmask); 2149 2150 int networkTypeBitmask = rilRadioTechnologyToNetworkTypeBitmask( 2151 Integer.parseInt(bearerStr)); 2152 cv.put(NETWORK_TYPE_BITMASK, networkTypeBitmask); 2153 } 2154 2155 int userEditedColumnIdx = c.getColumnIndex("user_edited"); 2156 if (userEditedColumnIdx != -1) { 2157 String user_edited = c.getString(userEditedColumnIdx); 2158 if (!TextUtils.isEmpty(user_edited)) { 2159 cv.put(EDITED_STATUS, new Integer(user_edited)); 2160 } 2161 } else { 2162 cv.put(EDITED_STATUS, CARRIER_EDITED); 2163 } 2164 2165 // New EDITED column. Default value (UNEDITED) will 2166 // be used for all rows except for non-mvno entries for plmns indicated 2167 // by resource: those will be set to CARRIER_EDITED to preserve 2168 // their current values 2169 val = c.getString(c.getColumnIndex(NUMERIC)); 2170 for (String s : persistApnsForPlmns) { 2171 if (!TextUtils.isEmpty(val) && val.equals(s) && 2172 (!cv.containsKey(MVNO_TYPE) || 2173 TextUtils.isEmpty(cv.getAsString(MVNO_TYPE)))) { 2174 if (userEditedColumnIdx == -1) { 2175 cv.put(EDITED_STATUS, CARRIER_EDITED); 2176 } else { // if (oldVersion == 14) -- if db had user_edited column 2177 if (cv.getAsInteger(EDITED_STATUS) == USER_EDITED) { 2178 cv.put(EDITED_STATUS, CARRIER_EDITED); 2179 } 2180 } 2181 2182 break; 2183 } 2184 } 2185 2186 try { 2187 db.insertWithOnConflict(CARRIERS_TABLE_TMP, null, cv, 2188 SQLiteDatabase.CONFLICT_ABORT); 2189 if (VDBG) { 2190 log("dbh.copyPreservedApnsToNewTable: db.insert returned >= 0; " + 2191 "insert successful for cv " + cv); 2192 } 2193 } catch (SQLException e) { 2194 if (VDBG) 2195 log("dbh.copyPreservedApnsToNewTable insertWithOnConflict exception " + 2196 e + " for cv " + cv); 2197 // Insertion failed which could be due to a conflict. Check if that is 2198 // the case and merge the entries 2199 Cursor oldRow = selectConflictingRow(db, 2200 CARRIERS_TABLE_TMP, cv); 2201 if (oldRow != null) { 2202 ContentValues mergedValues = new ContentValues(); 2203 mergeFieldsAndUpdateDb(db, CARRIERS_TABLE_TMP, oldRow, cv, 2204 mergedValues, true, mContext); 2205 oldRow.close(); 2206 } 2207 } 2208 } 2209 } catch (Resources.NotFoundException e) { 2210 loge("array.persist_apns_for_plmn is not found"); 2211 return; 2212 } 2213 } 2214 } 2215 getStringValueFromCursor(ContentValues cv, Cursor c, String key)2216 private void getStringValueFromCursor(ContentValues cv, Cursor c, String key) { 2217 int columnIndex = c.getColumnIndex(key); 2218 if (columnIndex != -1) { 2219 String fromCursor = c.getString(columnIndex); 2220 if (fromCursor != null) { 2221 cv.put(key, fromCursor); 2222 } 2223 } 2224 } 2225 2226 /** 2227 * If NETWORK_TYPE_BITMASK does not exist (upgrade from version 23 to version 24), generate 2228 * NETWORK_TYPE_BITMASK with the use of BEARER_BITMASK. If NETWORK_TYPE_BITMASK existed 2229 * (upgrade from version 24 to forward), always map NETWORK_TYPE_BITMASK to BEARER_BITMASK. 2230 */ getNetworkTypeBitmaskFromCursor(ContentValues cv, Cursor c)2231 private void getNetworkTypeBitmaskFromCursor(ContentValues cv, Cursor c) { 2232 int columnIndex = c.getColumnIndex(NETWORK_TYPE_BITMASK); 2233 if (columnIndex != -1) { 2234 getStringValueFromCursor(cv, c, NETWORK_TYPE_BITMASK); 2235 // Map NETWORK_TYPE_BITMASK to BEARER_BITMASK if NETWORK_TYPE_BITMASK existed; 2236 String fromCursor = c.getString(columnIndex); 2237 if (!TextUtils.isEmpty(fromCursor) && fromCursor.matches("\\d+")) { 2238 int networkBitmask = Integer.valueOf(fromCursor); 2239 int bearerBitmask = convertNetworkTypeBitmaskToBearerBitmask(networkBitmask); 2240 cv.put(BEARER_BITMASK, String.valueOf(bearerBitmask)); 2241 } 2242 return; 2243 } 2244 columnIndex = c.getColumnIndex(BEARER_BITMASK); 2245 if (columnIndex != -1) { 2246 String fromCursor = c.getString(columnIndex); 2247 if (!TextUtils.isEmpty(fromCursor) && fromCursor.matches("\\d+")) { 2248 int bearerBitmask = Integer.valueOf(fromCursor); 2249 int networkBitmask = convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask); 2250 cv.put(NETWORK_TYPE_BITMASK, String.valueOf(networkBitmask)); 2251 } 2252 } 2253 } 2254 getIntValueFromCursor(ContentValues cv, Cursor c, String key)2255 private void getIntValueFromCursor(ContentValues cv, Cursor c, String key) { 2256 int columnIndex = c.getColumnIndex(key); 2257 if (columnIndex != -1) { 2258 String fromCursor = c.getString(columnIndex); 2259 if (!TextUtils.isEmpty(fromCursor)) { 2260 try { 2261 cv.put(key, new Integer(fromCursor)); 2262 } catch (NumberFormatException nfe) { 2263 // do nothing 2264 } 2265 } 2266 } 2267 } 2268 getBlobValueFromCursor(ContentValues cv, Cursor c, String key)2269 private void getBlobValueFromCursor(ContentValues cv, Cursor c, String key) { 2270 int columnIndex = c.getColumnIndex(key); 2271 if (columnIndex != -1) { 2272 byte[] fromCursor = c.getBlob(columnIndex); 2273 if (fromCursor != null) { 2274 cv.put(key, fromCursor); 2275 } 2276 } 2277 } 2278 2279 /** 2280 * Gets the next row of apn values. 2281 * 2282 * @param parser the parser 2283 * @param isOverlay If the xml file comes from an overlay MCC/MNC are treated as integers 2284 * @return the row or null if it's not an apn 2285 */ getRow(XmlPullParser parser, boolean isOverlay)2286 private ContentValues getRow(XmlPullParser parser, boolean isOverlay) { 2287 if (!"apn".equals(parser.getName())) { 2288 return null; 2289 } 2290 2291 ContentValues map = new ContentValues(); 2292 2293 String mcc = parser.getAttributeValue(null, "mcc"); 2294 String mnc = parser.getAttributeValue(null, "mnc"); 2295 String mccString = mcc; 2296 String mncString = mnc; 2297 // Since an mnc can have both two and three digits and it is hard to verify 2298 // all OEM's Global APN lists we only do this for overlays. 2299 if (isOverlay) { 2300 mccString = String.format("%03d", Integer.parseInt(mcc)); 2301 // Looks up a two digit mnc in the carrier id DB 2302 // if not found a three digit mnc value is chosen 2303 mncString = getBestStringMnc(mContext, mccString, Integer.parseInt(mnc)); 2304 } 2305 2306 String numeric = mccString + mncString; 2307 map.put(NUMERIC, numeric); 2308 map.put(MCC, mccString); 2309 map.put(MNC, mncString); 2310 map.put(NAME, parser.getAttributeValue(null, "carrier")); 2311 2312 // do not add NULL to the map so that default values can be inserted in db 2313 addStringAttribute(parser, "apn", map, APN); 2314 addStringAttribute(parser, "user", map, USER); 2315 addStringAttribute(parser, "server", map, SERVER); 2316 addStringAttribute(parser, "password", map, PASSWORD); 2317 addStringAttribute(parser, "proxy", map, PROXY); 2318 addStringAttribute(parser, "port", map, PORT); 2319 addStringAttribute(parser, "mmsproxy", map, MMSPROXY); 2320 addStringAttribute(parser, "mmsport", map, MMSPORT); 2321 addStringAttribute(parser, "mmsc", map, MMSC); 2322 2323 String apnType = parser.getAttributeValue(null, "type"); 2324 if (apnType != null) { 2325 // Remove spaces before putting it in the map. 2326 apnType = apnType.replaceAll("\\s+", ""); 2327 map.put(TYPE, apnType); 2328 } 2329 2330 addStringAttribute(parser, "protocol", map, PROTOCOL); 2331 addStringAttribute(parser, "roaming_protocol", map, ROAMING_PROTOCOL); 2332 2333 addIntAttribute(parser, "authtype", map, AUTH_TYPE); 2334 addIntAttribute(parser, "bearer", map, BEARER); 2335 addIntAttribute(parser, "profile_id", map, PROFILE_ID); 2336 addIntAttribute(parser, "max_conns", map, MAX_CONNECTIONS); 2337 addIntAttribute(parser, "wait_time", map, WAIT_TIME_RETRY); 2338 addIntAttribute(parser, "max_conns_time", map, TIME_LIMIT_FOR_MAX_CONNECTIONS); 2339 addIntAttribute(parser, "mtu", map, MTU); 2340 addIntAttribute(parser, "apn_set_id", map, APN_SET_ID); 2341 addIntAttribute(parser, "carrier_id", map, CARRIER_ID); 2342 addIntAttribute(parser, "skip_464xlat", map, SKIP_464XLAT); 2343 2344 addBoolAttribute(parser, "carrier_enabled", map, CARRIER_ENABLED); 2345 addBoolAttribute(parser, "modem_cognitive", map, MODEM_PERSIST); 2346 addBoolAttribute(parser, "user_visible", map, USER_VISIBLE); 2347 addBoolAttribute(parser, "user_editable", map, USER_EDITABLE); 2348 2349 int networkTypeBitmask = 0; 2350 String networkTypeList = parser.getAttributeValue(null, "network_type_bitmask"); 2351 if (networkTypeList != null) { 2352 networkTypeBitmask = getBitmaskFromString(networkTypeList); 2353 } 2354 map.put(NETWORK_TYPE_BITMASK, networkTypeBitmask); 2355 2356 int bearerBitmask = 0; 2357 if (networkTypeList != null) { 2358 bearerBitmask = convertNetworkTypeBitmaskToBearerBitmask(networkTypeBitmask); 2359 } else { 2360 String bearerList = parser.getAttributeValue(null, "bearer_bitmask"); 2361 if (bearerList != null) { 2362 bearerBitmask = getBitmaskFromString(bearerList); 2363 } 2364 // Update the network type bitmask to keep them sync. 2365 networkTypeBitmask = convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask); 2366 map.put(NETWORK_TYPE_BITMASK, networkTypeBitmask); 2367 } 2368 map.put(BEARER_BITMASK, bearerBitmask); 2369 2370 String mvno_type = parser.getAttributeValue(null, "mvno_type"); 2371 if (mvno_type != null) { 2372 String mvno_match_data = parser.getAttributeValue(null, "mvno_match_data"); 2373 if (mvno_match_data != null) { 2374 map.put(MVNO_TYPE, mvno_type); 2375 map.put(MVNO_MATCH_DATA, mvno_match_data); 2376 } 2377 } 2378 return map; 2379 } 2380 addStringAttribute(XmlPullParser parser, String att, ContentValues map, String key)2381 private void addStringAttribute(XmlPullParser parser, String att, 2382 ContentValues map, String key) { 2383 String val = parser.getAttributeValue(null, att); 2384 if (val != null) { 2385 map.put(key, val); 2386 } 2387 } 2388 addIntAttribute(XmlPullParser parser, String att, ContentValues map, String key)2389 private void addIntAttribute(XmlPullParser parser, String att, 2390 ContentValues map, String key) { 2391 String val = parser.getAttributeValue(null, att); 2392 if (val != null) { 2393 map.put(key, Integer.parseInt(val)); 2394 } 2395 } 2396 addBoolAttribute(XmlPullParser parser, String att, ContentValues map, String key)2397 private void addBoolAttribute(XmlPullParser parser, String att, 2398 ContentValues map, String key) { 2399 String val = parser.getAttributeValue(null, att); 2400 if (val != null) { 2401 map.put(key, Boolean.parseBoolean(val)); 2402 } 2403 } 2404 2405 /* 2406 * Loads apns from xml file into the database 2407 * 2408 * @param db the sqlite database to write to 2409 * @param parser the xml parser 2410 * @param isOverlay, if we are parsing an xml in an overlay 2411 */ loadApns(SQLiteDatabase db, XmlPullParser parser, boolean isOverlay)2412 private void loadApns(SQLiteDatabase db, XmlPullParser parser, boolean isOverlay) { 2413 if (parser != null) { 2414 try { 2415 db.beginTransaction(); 2416 XmlUtils.nextElement(parser); 2417 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 2418 ContentValues row = getRow(parser, isOverlay); 2419 if (row == null) { 2420 throw new XmlPullParserException("Expected 'apn' tag", parser, null); 2421 } 2422 insertAddingDefaults(db, row); 2423 XmlUtils.nextElement(parser); 2424 } 2425 db.setTransactionSuccessful(); 2426 } catch (XmlPullParserException e) { 2427 loge("Got XmlPullParserException while loading apns." + e); 2428 } catch (IOException e) { 2429 loge("Got IOException while loading apns." + e); 2430 } catch (SQLException e) { 2431 loge("Got SQLException while loading apns." + e); 2432 } finally { 2433 db.endTransaction(); 2434 } 2435 } 2436 } 2437 insertAddingDefaults(SQLiteDatabase db, ContentValues row)2438 private void insertAddingDefaults(SQLiteDatabase db, ContentValues row) { 2439 row = setDefaultValue(row); 2440 try { 2441 db.insertWithOnConflict(CARRIERS_TABLE, null, row, SQLiteDatabase.CONFLICT_ABORT); 2442 if (VDBG) log("dbh.insertAddingDefaults: db.insert returned >= 0; insert " + 2443 "successful for cv " + row); 2444 } catch (SQLException e) { 2445 if (VDBG) log("dbh.insertAddingDefaults: exception " + e); 2446 // Insertion failed which could be due to a conflict. Check if that is the case and 2447 // update edited field accordingly. 2448 // Search for the exact same entry and update edited field. 2449 // If it is USER_EDITED/CARRIER_EDITED change it to UNEDITED, 2450 // and if USER/CARRIER_DELETED change it to USER/CARRIER_DELETED_BUT_PRESENT_IN_XML. 2451 Cursor oldRow = selectConflictingRow(db, CARRIERS_TABLE, row); 2452 if (oldRow != null) { 2453 // Update the row 2454 ContentValues mergedValues = new ContentValues(); 2455 int edited = oldRow.getInt(oldRow.getColumnIndex(EDITED_STATUS)); 2456 int old_edited = edited; 2457 if (edited != UNEDITED) { 2458 if (edited == USER_DELETED) { 2459 // USER_DELETED_BUT_PRESENT_IN_XML indicates entry has been deleted 2460 // by user but present in apn xml file. 2461 edited = USER_DELETED_BUT_PRESENT_IN_XML; 2462 } else if (edited == CARRIER_DELETED) { 2463 // CARRIER_DELETED_BUT_PRESENT_IN_XML indicates entry has been deleted 2464 // by user but present in apn xml file. 2465 edited = CARRIER_DELETED_BUT_PRESENT_IN_XML; 2466 } 2467 mergedValues.put(EDITED_STATUS, edited); 2468 } 2469 2470 mergeFieldsAndUpdateDb(db, CARRIERS_TABLE, oldRow, row, mergedValues, false, 2471 mContext); 2472 2473 if (VDBG) log("dbh.insertAddingDefaults: old edited = " + old_edited 2474 + " new edited = " + edited); 2475 2476 oldRow.close(); 2477 } 2478 } 2479 } 2480 } 2481 mergeFieldsAndUpdateDb(SQLiteDatabase db, String table, Cursor oldRow, ContentValues newRow, ContentValues mergedValues, boolean onUpgrade, Context context)2482 public static void mergeFieldsAndUpdateDb(SQLiteDatabase db, String table, Cursor oldRow, 2483 ContentValues newRow, ContentValues mergedValues, 2484 boolean onUpgrade, Context context) { 2485 if (newRow.containsKey(TYPE)) { 2486 // Merge the types 2487 String oldType = oldRow.getString(oldRow.getColumnIndex(TYPE)); 2488 String newType = newRow.getAsString(TYPE); 2489 2490 if (!oldType.equalsIgnoreCase(newType)) { 2491 if (oldType.equals("") || newType.equals("")) { 2492 newRow.put(TYPE, ""); 2493 } else { 2494 String[] oldTypes = oldType.toLowerCase().split(","); 2495 String[] newTypes = newType.toLowerCase().split(","); 2496 2497 if (VDBG) { 2498 log("mergeFieldsAndUpdateDb: Calling separateRowsNeeded() oldType=" + 2499 oldType + " old bearer=" + oldRow.getInt(oldRow.getColumnIndex( 2500 BEARER_BITMASK)) + " old networkType=" + 2501 oldRow.getInt(oldRow.getColumnIndex(NETWORK_TYPE_BITMASK)) + 2502 " old profile_id=" + oldRow.getInt(oldRow.getColumnIndex( 2503 PROFILE_ID)) + " newRow " + newRow); 2504 } 2505 2506 // If separate rows are needed, do not need to merge any further 2507 if (separateRowsNeeded(db, table, oldRow, newRow, context, oldTypes, 2508 newTypes)) { 2509 if (VDBG) log("mergeFieldsAndUpdateDb: separateRowsNeeded() returned " + 2510 "true"); 2511 return; 2512 } 2513 2514 // Merge the 2 types 2515 ArrayList<String> mergedTypes = new ArrayList<String>(); 2516 mergedTypes.addAll(Arrays.asList(oldTypes)); 2517 for (String s : newTypes) { 2518 if (!mergedTypes.contains(s.trim())) { 2519 mergedTypes.add(s); 2520 } 2521 } 2522 StringBuilder mergedType = new StringBuilder(); 2523 for (int i = 0; i < mergedTypes.size(); i++) { 2524 mergedType.append((i == 0 ? "" : ",") + mergedTypes.get(i)); 2525 } 2526 newRow.put(TYPE, mergedType.toString()); 2527 } 2528 } 2529 mergedValues.put(TYPE, newRow.getAsString(TYPE)); 2530 } 2531 2532 if (newRow.containsKey(BEARER_BITMASK)) { 2533 int oldBearer = oldRow.getInt(oldRow.getColumnIndex(BEARER_BITMASK)); 2534 int newBearer = newRow.getAsInteger(BEARER_BITMASK); 2535 if (oldBearer != newBearer) { 2536 if (oldBearer == 0 || newBearer == 0) { 2537 newRow.put(BEARER_BITMASK, 0); 2538 } else { 2539 newRow.put(BEARER_BITMASK, (oldBearer | newBearer)); 2540 } 2541 } 2542 mergedValues.put(BEARER_BITMASK, newRow.getAsInteger(BEARER_BITMASK)); 2543 } 2544 2545 if (newRow.containsKey(NETWORK_TYPE_BITMASK)) { 2546 int oldBitmask = oldRow.getInt(oldRow.getColumnIndex(NETWORK_TYPE_BITMASK)); 2547 int newBitmask = newRow.getAsInteger(NETWORK_TYPE_BITMASK); 2548 if (oldBitmask != newBitmask) { 2549 if (oldBitmask == 0 || newBitmask == 0) { 2550 newRow.put(NETWORK_TYPE_BITMASK, 0); 2551 } else { 2552 newRow.put(NETWORK_TYPE_BITMASK, (oldBitmask | newBitmask)); 2553 } 2554 } 2555 mergedValues.put(NETWORK_TYPE_BITMASK, newRow.getAsInteger(NETWORK_TYPE_BITMASK)); 2556 } 2557 2558 if (newRow.containsKey(BEARER_BITMASK) 2559 && newRow.containsKey(NETWORK_TYPE_BITMASK)) { 2560 syncBearerBitmaskAndNetworkTypeBitmask(mergedValues); 2561 } 2562 2563 if (!onUpgrade) { 2564 // Do not overwrite a carrier or user edit with EDITED=UNEDITED 2565 if (newRow.containsKey(EDITED_STATUS)) { 2566 int oldEdited = oldRow.getInt(oldRow.getColumnIndex(EDITED_STATUS)); 2567 int newEdited = newRow.getAsInteger(EDITED_STATUS); 2568 if (newEdited == UNEDITED && (oldEdited == CARRIER_EDITED 2569 || oldEdited == CARRIER_DELETED 2570 || oldEdited == CARRIER_DELETED_BUT_PRESENT_IN_XML 2571 || oldEdited == USER_EDITED 2572 || oldEdited == USER_DELETED 2573 || oldEdited == USER_DELETED_BUT_PRESENT_IN_XML)) { 2574 newRow.remove(EDITED_STATUS); 2575 } 2576 } 2577 mergedValues.putAll(newRow); 2578 } 2579 2580 if (mergedValues.size() > 0) { 2581 db.update(table, mergedValues, "_id=" + oldRow.getInt(oldRow.getColumnIndex("_id")), 2582 null); 2583 } 2584 } 2585 separateRowsNeeded(SQLiteDatabase db, String table, Cursor oldRow, ContentValues newRow, Context context, String[] oldTypes, String[] newTypes)2586 private static boolean separateRowsNeeded(SQLiteDatabase db, String table, Cursor oldRow, 2587 ContentValues newRow, Context context, 2588 String[] oldTypes, String[] newTypes) { 2589 // If this APN falls under persist_apns_for_plmn, and the 2590 // only difference between old type and new type is that one has dun, and 2591 // the APNs have profile_id 0 or not set, then set the profile_id to 1 for 2592 // the dun APN/remove dun from type. This will ensure both oldRow and newRow exist 2593 // separately in db. 2594 2595 boolean match = false; 2596 2597 // Check if APN falls under persist_apns_for_plmn 2598 if (context.getResources() != null) { 2599 String[] persistApnsForPlmns = context.getResources().getStringArray( 2600 R.array.persist_apns_for_plmn); 2601 for (String s : persistApnsForPlmns) { 2602 if (s.equalsIgnoreCase(newRow.getAsString(NUMERIC))) { 2603 match = true; 2604 break; 2605 } 2606 } 2607 } else { 2608 loge("separateRowsNeeded: resources=null"); 2609 } 2610 2611 if (!match) return false; 2612 2613 // APN falls under persist_apns_for_plmn 2614 // Check if only difference between old type and new type is that 2615 // one has dun 2616 ArrayList<String> oldTypesAl = new ArrayList<String>(Arrays.asList(oldTypes)); 2617 ArrayList<String> newTypesAl = new ArrayList<String>(Arrays.asList(newTypes)); 2618 ArrayList<String> listWithDun = null; 2619 ArrayList<String> listWithoutDun = null; 2620 boolean dunInOld = false; 2621 if (oldTypesAl.size() == newTypesAl.size() + 1) { 2622 listWithDun = oldTypesAl; 2623 listWithoutDun = newTypesAl; 2624 dunInOld = true; 2625 } else if (oldTypesAl.size() + 1 == newTypesAl.size()) { 2626 listWithDun = newTypesAl; 2627 listWithoutDun = oldTypesAl; 2628 } else { 2629 return false; 2630 } 2631 2632 if (listWithDun.contains("dun") && !listWithoutDun.contains("dun")) { 2633 listWithoutDun.add("dun"); 2634 if (!listWithDun.containsAll(listWithoutDun)) { 2635 return false; 2636 } 2637 2638 // Only difference between old type and new type is that 2639 // one has dun 2640 // Check if profile_id is 0/not set 2641 if (oldRow.getInt(oldRow.getColumnIndex(PROFILE_ID)) == 0) { 2642 if (dunInOld) { 2643 // Update oldRow to remove dun from its type field 2644 ContentValues updateOldRow = new ContentValues(); 2645 StringBuilder sb = new StringBuilder(); 2646 boolean first = true; 2647 for (String s : listWithDun) { 2648 if (!s.equalsIgnoreCase("dun")) { 2649 sb.append(first ? s : "," + s); 2650 first = false; 2651 } 2652 } 2653 String updatedType = sb.toString(); 2654 if (VDBG) { 2655 log("separateRowsNeeded: updating type in oldRow to " + updatedType); 2656 } 2657 updateOldRow.put(TYPE, updatedType); 2658 db.update(table, updateOldRow, 2659 "_id=" + oldRow.getInt(oldRow.getColumnIndex("_id")), null); 2660 return true; 2661 } else { 2662 if (VDBG) log("separateRowsNeeded: adding profile id 1 to newRow"); 2663 // Update newRow to set profile_id to 1 2664 newRow.put(PROFILE_ID, new Integer(1)); 2665 } 2666 } else { 2667 return false; 2668 } 2669 2670 // If match was found, both oldRow and newRow need to exist 2671 // separately in db. Add newRow to db. 2672 try { 2673 db.insertWithOnConflict(table, null, newRow, SQLiteDatabase.CONFLICT_REPLACE); 2674 if (VDBG) log("separateRowsNeeded: added newRow with profile id 1 to db"); 2675 return true; 2676 } catch (SQLException e) { 2677 loge("Exception on trying to add new row after updating profile_id"); 2678 } 2679 } 2680 2681 return false; 2682 } 2683 selectConflictingRow(SQLiteDatabase db, String table, ContentValues row)2684 public static Cursor selectConflictingRow(SQLiteDatabase db, String table, 2685 ContentValues row) { 2686 // Conflict is possible only when numeric, mcc, mnc (fields without any default value) 2687 // are set in the new row 2688 if (!row.containsKey(NUMERIC) || !row.containsKey(MCC) || !row.containsKey(MNC)) { 2689 loge("dbh.selectConflictingRow: called for non-conflicting row: " + row); 2690 return null; 2691 } 2692 2693 String[] columns = { "_id", 2694 TYPE, 2695 EDITED_STATUS, 2696 BEARER_BITMASK, 2697 NETWORK_TYPE_BITMASK, 2698 PROFILE_ID }; 2699 String selection = TextUtils.join("=? AND ", CARRIERS_UNIQUE_FIELDS) + "=?"; 2700 int i = 0; 2701 String[] selectionArgs = new String[CARRIERS_UNIQUE_FIELDS.size()]; 2702 for (String field : CARRIERS_UNIQUE_FIELDS) { 2703 if (!row.containsKey(field)) { 2704 selectionArgs[i++] = CARRIERS_UNIQUE_FIELDS_DEFAULTS.get(field); 2705 } else { 2706 if (CARRIERS_BOOLEAN_FIELDS.contains(field)) { 2707 // for boolean fields we overwrite the strings "true" and "false" with "1" 2708 // and "0" 2709 selectionArgs[i++] = convertStringToIntString(row.getAsString(field)); 2710 } else { 2711 selectionArgs[i++] = row.getAsString(field); 2712 } 2713 } 2714 } 2715 2716 Cursor c = db.query(table, columns, selection, selectionArgs, null, null, null); 2717 2718 if (c != null) { 2719 if (c.getCount() == 1) { 2720 if (VDBG) log("dbh.selectConflictingRow: " + c.getCount() + " conflicting " + 2721 "row found"); 2722 if (c.moveToFirst()) { 2723 return c; 2724 } else { 2725 loge("dbh.selectConflictingRow: moveToFirst() failed"); 2726 } 2727 } else { 2728 loge("dbh.selectConflictingRow: Expected 1 but found " + c.getCount() + 2729 " matching rows found for cv " + row); 2730 } 2731 c.close(); 2732 } else { 2733 loge("dbh.selectConflictingRow: Error - c is null; no matching row found for " + 2734 "cv " + row); 2735 } 2736 2737 return null; 2738 } 2739 2740 /** 2741 * Convert "true" and "false" to "1" and "0". 2742 * If the passed in string is already "1" or "0" returns the passed in string. 2743 */ convertStringToIntString(String boolString)2744 private static String convertStringToIntString(String boolString) { 2745 if ("0".equals(boolString) || "false".equalsIgnoreCase(boolString)) return "0"; 2746 return "1"; 2747 } 2748 2749 /** 2750 * Convert "1" and "0" to "true" and "false". 2751 * If the passed in string is already "true" or "false" returns the passed in string. 2752 */ convertStringToBoolString(String intString)2753 private static String convertStringToBoolString(String intString) { 2754 if ("0".equals(intString) || "false".equalsIgnoreCase(intString)) return "false"; 2755 return "true"; 2756 } 2757 2758 /** 2759 * These methods can be overridden in a subclass for testing TelephonyProvider using an 2760 * in-memory database. 2761 */ getReadableDatabase()2762 SQLiteDatabase getReadableDatabase() { 2763 return mOpenHelper.getReadableDatabase(); 2764 } getWritableDatabase()2765 SQLiteDatabase getWritableDatabase() { 2766 return mOpenHelper.getWritableDatabase(); 2767 } initDatabaseWithDatabaseHelper(SQLiteDatabase db)2768 void initDatabaseWithDatabaseHelper(SQLiteDatabase db) { 2769 mOpenHelper.initDatabase(db); 2770 } needApnDbUpdate()2771 boolean needApnDbUpdate() { 2772 return mOpenHelper.apnDbUpdateNeeded(); 2773 } 2774 apnSourceServiceExists(Context context)2775 private static boolean apnSourceServiceExists(Context context) { 2776 if (s_apnSourceServiceExists != null) { 2777 return s_apnSourceServiceExists; 2778 } 2779 try { 2780 String service = context.getResources().getString(R.string.apn_source_service); 2781 if (TextUtils.isEmpty(service)) { 2782 s_apnSourceServiceExists = false; 2783 } else { 2784 s_apnSourceServiceExists = context.getPackageManager().getServiceInfo( 2785 ComponentName.unflattenFromString(service), 0) 2786 != null; 2787 } 2788 } catch (PackageManager.NameNotFoundException e) { 2789 s_apnSourceServiceExists = false; 2790 } 2791 return s_apnSourceServiceExists; 2792 } 2793 restoreApnsWithService(int subId)2794 private void restoreApnsWithService(int subId) { 2795 Context context = getContext(); 2796 Resources r = context.getResources(); 2797 AtomicBoolean connectionBindingInvalid = new AtomicBoolean(false); 2798 ServiceConnection connection = new ServiceConnection() { 2799 @Override 2800 public void onServiceConnected(ComponentName className, 2801 IBinder service) { 2802 log("restoreApnsWithService: onServiceConnected"); 2803 synchronized (mLock) { 2804 mIApnSourceService = IApnSourceService.Stub.asInterface(service); 2805 mLock.notifyAll(); 2806 } 2807 } 2808 2809 @Override 2810 public void onServiceDisconnected(ComponentName arg0) { 2811 loge("mIApnSourceService has disconnected unexpectedly"); 2812 synchronized (mLock) { 2813 mIApnSourceService = null; 2814 } 2815 } 2816 2817 @Override 2818 public void onBindingDied(ComponentName name) { 2819 loge("The binding to the apn service connection is dead: " + name); 2820 synchronized (mLock) { 2821 connectionBindingInvalid.set(true); 2822 mLock.notifyAll(); 2823 } 2824 } 2825 2826 @Override 2827 public void onNullBinding(ComponentName name) { 2828 loge("Null binding: " + name); 2829 synchronized (mLock) { 2830 connectionBindingInvalid.set(true); 2831 mLock.notifyAll(); 2832 } 2833 } 2834 }; 2835 2836 Intent intent = new Intent(IApnSourceService.class.getName()); 2837 intent.setComponent(ComponentName.unflattenFromString( 2838 r.getString(R.string.apn_source_service))); 2839 log("binding to service to restore apns, intent=" + intent); 2840 try { 2841 if (context.bindService(intent, 2842 Context.BIND_IMPORTANT | Context.BIND_AUTO_CREATE, 2843 runnable -> new Thread(runnable).start(), 2844 connection)) { 2845 synchronized (mLock) { 2846 while (mIApnSourceService == null && !connectionBindingInvalid.get()) { 2847 try { 2848 mLock.wait(); 2849 } catch (InterruptedException e) { 2850 loge("Error while waiting for service connection: " + e); 2851 } 2852 } 2853 if (connectionBindingInvalid.get()) { 2854 loge("The binding is invalid."); 2855 return; 2856 } 2857 try { 2858 ContentValues[] values = mIApnSourceService.getApns(subId); 2859 if (values != null) { 2860 // we use the unsynchronized insert because this function is called 2861 // within the syncrhonized function delete() 2862 unsynchronizedBulkInsert(CONTENT_URI, values); 2863 log("restoreApnsWithService: restored"); 2864 } 2865 } catch (RemoteException e) { 2866 loge("Error applying apns from service: " + e); 2867 } 2868 } 2869 } else { 2870 loge("unable to bind to service from intent=" + intent); 2871 } 2872 } catch (SecurityException e) { 2873 loge("Error applying apns from service: " + e); 2874 } finally { 2875 if (connection != null) { 2876 context.unbindService(connection); 2877 } 2878 synchronized (mLock) { 2879 mIApnSourceService = null; 2880 } 2881 } 2882 } 2883 2884 2885 @Override onCreate()2886 public boolean onCreate() { 2887 mOpenHelper = new DatabaseHelper(getContext()); 2888 2889 try { 2890 PhoneFactory.addLocalLog(TAG, 100); 2891 } catch (IllegalArgumentException e) { 2892 // ignore 2893 } 2894 2895 boolean isNewBuild = false; 2896 String newBuildId = SystemProperties.get("ro.build.id", null); 2897 SharedPreferences sp = getContext().getSharedPreferences(BUILD_ID_FILE, 2898 Context.MODE_PRIVATE); 2899 if (!TextUtils.isEmpty(newBuildId)) { 2900 // Check if build id has changed 2901 String oldBuildId = sp.getString(RO_BUILD_ID, ""); 2902 if (!newBuildId.equals(oldBuildId)) { 2903 localLog("onCreate: build id changed from " + oldBuildId + " to " + newBuildId); 2904 isNewBuild = true; 2905 } else { 2906 if (VDBG) log("onCreate: build id did not change: " + oldBuildId); 2907 } 2908 } else { 2909 if (VDBG) log("onCreate: newBuildId is empty"); 2910 } 2911 2912 if (isNewBuild) { 2913 if (!apnSourceServiceExists(getContext())) { 2914 // Update APN DB 2915 updateApnDb(); 2916 } 2917 2918 // Add all APN related shared prefs to local log for dumpsys 2919 if (DBG) addAllApnSharedPrefToLocalLog(); 2920 } 2921 2922 // Write build id to SharedPreferences after APNs have been updated above by updateApnDb() 2923 if (!TextUtils.isEmpty(newBuildId)) { 2924 if (isNewBuild) log("onCreate: updating build id to " + newBuildId); 2925 sp.edit().putString(RO_BUILD_ID, newBuildId).apply(); 2926 } 2927 2928 SharedPreferences spEnforcedFile = getContext().getSharedPreferences(ENFORCED_FILE, 2929 Context.MODE_PRIVATE); 2930 mManagedApnEnforced = spEnforcedFile.getBoolean(ENFORCED_KEY, false); 2931 2932 if (VDBG) log("onCreate:- ret true"); 2933 2934 return true; 2935 } 2936 addAllApnSharedPrefToLocalLog()2937 private void addAllApnSharedPrefToLocalLog() { 2938 localLog("addAllApnSharedPrefToLocalLog"); 2939 SharedPreferences spApn = getContext().getSharedPreferences(PREF_FILE_APN, 2940 Context.MODE_PRIVATE); 2941 2942 Map<String, ?> allPrefApnId = spApn.getAll(); 2943 for (String key : allPrefApnId.keySet()) { 2944 try { 2945 localLog(key + ":" + allPrefApnId.get(key).toString()); 2946 } catch (Exception e) { 2947 localLog("Skipping over key " + key + " due to exception " + e); 2948 } 2949 } 2950 2951 SharedPreferences spFullApn = getContext().getSharedPreferences(PREF_FILE_FULL_APN, 2952 Context.MODE_PRIVATE); 2953 2954 Map<String, ?> allPrefFullApn = spFullApn.getAll(); 2955 for (String key : allPrefFullApn.keySet()) { 2956 try { 2957 localLog(key + ":" + allPrefFullApn.get(key).toString()); 2958 } catch (Exception e) { 2959 localLog("Skipping over key " + key + " due to exception " + e); 2960 } 2961 } 2962 } 2963 localLog(String logMsg)2964 private static void localLog(String logMsg) { 2965 Log.d(TAG, logMsg); 2966 PhoneFactory.localLog(TAG, logMsg); 2967 } 2968 isManagedApnEnforced()2969 private synchronized boolean isManagedApnEnforced() { 2970 return mManagedApnEnforced; 2971 } 2972 setManagedApnEnforced(boolean enforced)2973 private void setManagedApnEnforced(boolean enforced) { 2974 SharedPreferences sp = getContext().getSharedPreferences(ENFORCED_FILE, 2975 Context.MODE_PRIVATE); 2976 SharedPreferences.Editor editor = sp.edit(); 2977 editor.putBoolean(ENFORCED_KEY, enforced); 2978 editor.apply(); 2979 synchronized (this) { 2980 mManagedApnEnforced = enforced; 2981 } 2982 } 2983 setPreferredApnId(Long id, int subId, boolean saveApn)2984 private void setPreferredApnId(Long id, int subId, boolean saveApn) { 2985 SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_APN, 2986 Context.MODE_PRIVATE); 2987 SharedPreferences.Editor editor = sp.edit(); 2988 editor.putLong(COLUMN_APN_ID + subId, id != null ? id : INVALID_APN_ID); 2989 localLog("setPreferredApnId: " + COLUMN_APN_ID + subId + ":" 2990 + (id != null ? id : INVALID_APN_ID)); 2991 // This is for debug purposes. It indicates if this APN was set by DcTracker or user (true) 2992 // or if this was restored from APN saved in PREF_FILE_FULL_APN (false). 2993 editor.putBoolean(EXPLICIT_SET_CALLED + subId, saveApn); 2994 localLog("setPreferredApnId: " + EXPLICIT_SET_CALLED + subId + ":" + saveApn); 2995 editor.apply(); 2996 if (id == null || id.longValue() == INVALID_APN_ID) { 2997 deletePreferredApn(subId); 2998 } else { 2999 // If id is not invalid, and saveApn is true, save the actual APN in PREF_FILE_FULL_APN 3000 // too. 3001 if (saveApn) { 3002 setPreferredApn(id, subId); 3003 } 3004 } 3005 } 3006 getPreferredApnId(int subId, boolean checkApnSp)3007 private long getPreferredApnId(int subId, boolean checkApnSp) { 3008 SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_APN, 3009 Context.MODE_PRIVATE); 3010 long apnId = sp.getLong(COLUMN_APN_ID + subId, INVALID_APN_ID); 3011 if (apnId == INVALID_APN_ID && checkApnSp) { 3012 apnId = getPreferredApnIdFromApn(subId); 3013 if (apnId != INVALID_APN_ID) { 3014 setPreferredApnId(apnId, subId, false); 3015 } 3016 } 3017 return apnId; 3018 } 3019 getPreferredApnSetId(int subId)3020 private int getPreferredApnSetId(int subId) { 3021 SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_FULL_APN, 3022 Context.MODE_PRIVATE); 3023 try { 3024 return Integer.parseInt(sp.getString(APN_SET_ID + subId, null)); 3025 } catch (NumberFormatException e) { 3026 return NO_APN_SET_ID; 3027 } 3028 } 3029 deletePreferredApnId(Context context)3030 private void deletePreferredApnId(Context context) { 3031 SharedPreferences sp = context.getSharedPreferences(PREF_FILE_APN, 3032 Context.MODE_PRIVATE); 3033 SharedPreferences.Editor editor = sp.edit(); 3034 editor.clear(); 3035 editor.apply(); 3036 } 3037 setPreferredApn(Long id, int subId)3038 private void setPreferredApn(Long id, int subId) { 3039 localLog("setPreferredApn: _id " + id + " subId " + subId); 3040 SQLiteDatabase db = getWritableDatabase(); 3041 // query all unique fields from id 3042 String[] proj = CARRIERS_UNIQUE_FIELDS.toArray(new String[CARRIERS_UNIQUE_FIELDS.size()]); 3043 3044 Cursor c = db.query(CARRIERS_TABLE, proj, "_id=" + id, null, null, null, null); 3045 if (c != null) { 3046 if (c.getCount() == 1) { 3047 c.moveToFirst(); 3048 SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_FULL_APN, 3049 Context.MODE_PRIVATE); 3050 SharedPreferences.Editor editor = sp.edit(); 3051 // store values of all unique fields to SP 3052 for (String key : CARRIERS_UNIQUE_FIELDS) { 3053 editor.putString(key + subId, c.getString(c.getColumnIndex(key))); 3054 localLog("setPreferredApn: " + key + subId + ":" 3055 + c.getString(c.getColumnIndex(key))); 3056 } 3057 // also store the version number 3058 editor.putString(DB_VERSION_KEY + subId, "" + DATABASE_VERSION); 3059 localLog("setPreferredApn: " + DB_VERSION_KEY + subId + ":" + DATABASE_VERSION); 3060 editor.apply(); 3061 } else { 3062 log("setPreferredApn: # matching APNs found " + c.getCount()); 3063 } 3064 c.close(); 3065 } else { 3066 log("setPreferredApn: No matching APN found"); 3067 } 3068 } 3069 getPreferredApnIdFromApn(int subId)3070 private long getPreferredApnIdFromApn(int subId) { 3071 log("getPreferredApnIdFromApn: for subId " + subId); 3072 SQLiteDatabase db = getReadableDatabase(); 3073 3074 List<String> whereList = new ArrayList<>(); 3075 List<String> whereArgsList = new ArrayList<>(); 3076 SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_FULL_APN, 3077 Context.MODE_PRIVATE); 3078 for (String key : CARRIERS_UNIQUE_FIELDS) { 3079 String value = sp.getString(key + subId, null); 3080 if (value == null) { 3081 continue; 3082 } else { 3083 whereList.add(key); 3084 whereArgsList.add(value); 3085 } 3086 } 3087 if (whereList.size() == 0) return INVALID_APN_ID; 3088 3089 String where = TextUtils.join("=? and ", whereList) + "=?"; 3090 String[] whereArgs = new String[whereArgsList.size()]; 3091 whereArgs = whereArgsList.toArray(whereArgs); 3092 3093 long apnId = INVALID_APN_ID; 3094 Cursor c = db.query(CARRIERS_TABLE, new String[]{"_id"}, where, whereArgs, null, null, 3095 null); 3096 if (c != null) { 3097 if (c.getCount() == 1) { 3098 c.moveToFirst(); 3099 apnId = c.getInt(c.getColumnIndex("_id")); 3100 } else { 3101 log("getPreferredApnIdFromApn: returning INVALID. # matching APNs found " + 3102 c.getCount()); 3103 } 3104 c.close(); 3105 } else { 3106 log("getPreferredApnIdFromApn: returning INVALID. No matching APN found"); 3107 } 3108 return apnId; 3109 } 3110 deletePreferredApn(int subId)3111 private void deletePreferredApn(int subId) { 3112 log("deletePreferredApn: for subId " + subId); 3113 SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_FULL_APN, 3114 Context.MODE_PRIVATE); 3115 if (sp.contains(DB_VERSION_KEY + subId)) { 3116 log("deletePreferredApn: apn is stored. Deleting it now for subId " + subId); 3117 SharedPreferences.Editor editor = sp.edit(); 3118 editor.remove(DB_VERSION_KEY + subId); 3119 for (String key : CARRIERS_UNIQUE_FIELDS) { 3120 editor.remove(key + subId); 3121 } 3122 editor.apply(); 3123 } 3124 } 3125 isCallingFromSystemOrPhoneUid()3126 boolean isCallingFromSystemOrPhoneUid() { 3127 return mInjector.binderGetCallingUid() == Process.SYSTEM_UID || 3128 mInjector.binderGetCallingUid() == Process.PHONE_UID; 3129 } 3130 ensureCallingFromSystemOrPhoneUid(String message)3131 void ensureCallingFromSystemOrPhoneUid(String message) { 3132 if (!isCallingFromSystemOrPhoneUid()) { 3133 throw new SecurityException(message); 3134 } 3135 } 3136 3137 @Override call(String method, @Nullable String args, @Nullable Bundle bundle)3138 public synchronized Bundle call(String method, @Nullable String args, @Nullable Bundle bundle) { 3139 if (SubscriptionManager.GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME.equals(method)) { 3140 getContext().enforceCallingOrSelfPermission( 3141 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, TAG); 3142 final long identity = Binder.clearCallingIdentity(); 3143 try { 3144 return retrieveSimSpecificSettings(); 3145 } finally { 3146 Binder.restoreCallingIdentity(identity); 3147 } 3148 } else if (SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME.equals(method)) { 3149 getContext().enforceCallingOrSelfPermission( 3150 android.Manifest.permission.MODIFY_PHONE_STATE, TAG); 3151 final long identity = Binder.clearCallingIdentity(); 3152 try { 3153 restoreSimSpecificSettings(bundle, args); 3154 } finally { 3155 Binder.restoreCallingIdentity(identity); 3156 } 3157 } else { 3158 loge("method is not recognized"); 3159 } 3160 3161 return null; 3162 } 3163 3164 /** 3165 * See {@link SubscriptionController#GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME} for details 3166 */ retrieveSimSpecificSettings()3167 private Bundle retrieveSimSpecificSettings() { 3168 Bundle resultBundle = new Bundle(); 3169 resultBundle.putByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA, 3170 getSimSpecificDataToBackUp()); 3171 3172 return resultBundle; 3173 } 3174 3175 /** 3176 * Attempts to restore the backed up sim-specific configs to device. End result is SimInfoDB is 3177 * modified to match any backed up configs for the appropriate inserted sims. 3178 * 3179 * @param bundle containing the data to be restored. If {@code null}, then backed up 3180 * data should already be in internal storage and will be retrieved from there. 3181 * @param iccId of the SIM that a restore is being attempted for. If {@code null}, then try to 3182 * restore for all simInfo entries in SimInfoDB 3183 */ restoreSimSpecificSettings(@ullable Bundle bundle, @Nullable String iccId)3184 private void restoreSimSpecificSettings(@Nullable Bundle bundle, @Nullable String iccId) { 3185 int restoreCase = TelephonyProtoEnums.SIM_RESTORE_CASE_UNDEFINED_USE_CASE; 3186 if (bundle != null) { 3187 restoreCase = TelephonyProtoEnums.SIM_RESTORE_CASE_SUW; 3188 if (!writeSimSettingsToInternalStorage( 3189 bundle.getByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA))) { 3190 return; 3191 } 3192 } else if (iccId != null){ 3193 restoreCase = TelephonyProtoEnums.SIM_RESTORE_CASE_SIM_INSERTED; 3194 } 3195 mergeBackedUpDataToSimInfoDb(restoreCase, iccId); 3196 } 3197 3198 @VisibleForTesting writeSimSettingsToInternalStorage(byte[] data)3199 boolean writeSimSettingsToInternalStorage(byte[] data) { 3200 AtomicFile atomicFile = new AtomicFile( 3201 new File(getContext().getFilesDir(), BACKED_UP_SIM_SPECIFIC_SETTINGS_FILE)); 3202 FileOutputStream fos = null; 3203 try { 3204 fos = atomicFile.startWrite(); 3205 fos.write(data); 3206 atomicFile.finishWrite(fos); 3207 } catch (IOException e) { 3208 if (fos != null) { 3209 atomicFile.failWrite(fos); 3210 } 3211 loge("Not able to create internal file with per-sim configs. Failed with error " 3212 + e); 3213 return false; 3214 } 3215 3216 return true; 3217 } 3218 3219 /** 3220 * Attempt to match any SimInfoDB entries to what is in the internal backup data file and 3221 * update DB entry with the adequate backed up data. 3222 * 3223 * @param restoreCase one of the SimSpecificSettingsRestoreMatchingCriteria values defined in 3224 * frameworks/proto_logging/stats/enums/telephony/enums.proto 3225 * @param iccId of the SIM that a restore is being attempted for. If {@code null}, then try to 3226 * restore for all simInfo entries in SimInfoDB 3227 */ mergeBackedUpDataToSimInfoDb(int restoreCase, @Nullable String iccId)3228 private void mergeBackedUpDataToSimInfoDb(int restoreCase, @Nullable String iccId) { 3229 // Get data stored in internal file 3230 File file = new File(getContext().getFilesDir(), BACKED_UP_SIM_SPECIFIC_SETTINGS_FILE); 3231 if (!file.exists()) { 3232 loge("internal sim-specific settings backup data file does not exist. " 3233 + "Aborting restore"); 3234 return; 3235 } 3236 3237 AtomicFile atomicFile = new AtomicFile(file); 3238 PersistableBundle bundle = null; 3239 try (FileInputStream fis = atomicFile.openRead()) { 3240 bundle = PersistableBundle.readFromStream(fis); 3241 } catch (IOException e) { 3242 loge("Failed to convert backed up per-sim configs to bundle. Stopping restore. " 3243 + "Failed with error " + e); 3244 return; 3245 } 3246 3247 String selection = null; 3248 String[] selectionArgs = null; 3249 if (iccId != null) { 3250 selection = Telephony.SimInfo.COLUMN_ICC_ID + "=?"; 3251 selectionArgs = new String[]{iccId}; 3252 } 3253 try (Cursor cursor = query( 3254 SubscriptionManager.CONTENT_URI, 3255 new String[]{ 3256 Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID, 3257 Telephony.SimInfo.COLUMN_ICC_ID, 3258 Telephony.SimInfo.COLUMN_NUMBER, 3259 Telephony.SimInfo.COLUMN_CARRIER_ID, 3260 Telephony.SimInfo.COLUMN_ISO_COUNTRY_CODE}, 3261 selection, 3262 selectionArgs, 3263 ORDER_BY_SUB_ID)) { 3264 findAndRestoreAllMatches(bundle.deepCopy(), cursor, restoreCase); 3265 } 3266 } 3267 3268 // backedUpDataBundle must to be mutable findAndRestoreAllMatches(PersistableBundle backedUpDataBundle, Cursor cursor, int restoreCase)3269 private void findAndRestoreAllMatches(PersistableBundle backedUpDataBundle, Cursor cursor, 3270 int restoreCase) { 3271 int[] previouslyRestoredSubIdsArray = 3272 backedUpDataBundle.getIntArray(KEY_PREVIOUSLY_RESTORED_SUB_IDS); 3273 List<Integer> previouslyRestoredSubIdsList = previouslyRestoredSubIdsArray != null 3274 ? Arrays.stream(previouslyRestoredSubIdsArray).boxed().collect(Collectors.toList()) 3275 : new ArrayList<>(); 3276 List<Integer> newlyRestoredSubIds = new ArrayList<>(); 3277 int backupDataFormatVersion = backedUpDataBundle 3278 .getInt(KEY_BACKUP_DATA_FORMAT_VERSION, -1); 3279 3280 Resources r = getContext().getResources(); 3281 List<String> wfcRestoreBlockedCountries = Arrays.asList(r.getStringArray( 3282 R.array.wfc_restore_blocked_countries)); 3283 3284 while (cursor != null && cursor.moveToNext()) { 3285 // Get all the possible matching criteria. 3286 int subIdColumnIndex = cursor.getColumnIndex( 3287 Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID); 3288 int currSubIdFromDb = cursor.getInt(subIdColumnIndex); 3289 3290 if (previouslyRestoredSubIdsList.contains(currSubIdFromDb)) { 3291 // Abort restore for any sims that were previously restored. 3292 continue; 3293 } 3294 3295 int iccIdColumnIndex = cursor.getColumnIndex(Telephony.SimInfo.COLUMN_ICC_ID); 3296 String currIccIdFromDb = cursor.getString(iccIdColumnIndex); 3297 3298 int phoneNumberColumnIndex = cursor.getColumnIndex(Telephony.SimInfo.COLUMN_NUMBER); 3299 String currPhoneNumberFromDb = cursor.getString(phoneNumberColumnIndex); 3300 3301 int carrierIdColumnIndex = cursor.getColumnIndex(Telephony.SimInfo.COLUMN_CARRIER_ID); 3302 int currCarrierIdFromDb = cursor.getInt(carrierIdColumnIndex); 3303 3304 int isoCountryCodeColumnIndex= cursor.getColumnIndex( 3305 Telephony.SimInfo.COLUMN_ISO_COUNTRY_CODE); 3306 String isoCountryCodeFromDb = cursor.getString(isoCountryCodeColumnIndex); 3307 3308 3309 // Find the best match from backed up data. 3310 SimRestoreMatch bestRestoreMatch = null; 3311 for (int rowNum = 0; true; rowNum++) { 3312 PersistableBundle currRow = backedUpDataBundle.getPersistableBundle( 3313 KEY_SIMINFO_DB_ROW_PREFIX + rowNum); 3314 if (currRow == null) { 3315 break; 3316 } 3317 3318 SimRestoreMatch currSimRestoreMatch = new SimRestoreMatch( 3319 currIccIdFromDb, currCarrierIdFromDb, currPhoneNumberFromDb, 3320 isoCountryCodeFromDb, wfcRestoreBlockedCountries, currRow, 3321 backupDataFormatVersion); 3322 3323 if (currSimRestoreMatch == null) { 3324 continue; 3325 } 3326 3327 /* 3328 * The three following match cases are ordered by descending priority: 3329 * - Match by iccId: If iccId of backup data matches iccId of any inserted sims, 3330 * we confidently restore all configs. 3331 * - Match phone number and carrierId: If both of these values match, we 3332 * confidently restore all configs. 3333 * - Match only carrierId: If only carrierId of backup data matches an inserted 3334 * sim, we only restore non-sensitive configs. 3335 * 3336 * Having a matchScore value for each match allows us to control these priorities. 3337 */ 3338 if (bestRestoreMatch == null || (currSimRestoreMatch.getMatchScore() 3339 >= bestRestoreMatch.getMatchScore() 3340 && currSimRestoreMatch.getContentValues() != null)) { 3341 bestRestoreMatch = currSimRestoreMatch; 3342 } 3343 } 3344 3345 if (bestRestoreMatch != null) { 3346 if (bestRestoreMatch.getMatchScore() != 0) { 3347 if (restoreCase == TelephonyProtoEnums.SIM_RESTORE_CASE_SUW) { 3348 update(SubscriptionManager.SIM_INFO_SUW_RESTORE_CONTENT_URI, 3349 bestRestoreMatch.getContentValues(), 3350 Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=?", 3351 new String[]{Integer.toString(currSubIdFromDb)}); 3352 } else if (restoreCase == TelephonyProtoEnums.SIM_RESTORE_CASE_SIM_INSERTED) { 3353 Uri simInsertedRestoreUri = Uri.withAppendedPath( 3354 SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, 3355 SIM_INSERTED_RESTORE_URI_SUFFIX); 3356 update(simInsertedRestoreUri, 3357 bestRestoreMatch.getContentValues(), 3358 Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=?", 3359 new String[]{Integer.toString(currSubIdFromDb)}); 3360 } 3361 log("Restore of inserterd SIM's sim-specific settings has been successfully " 3362 + "completed."); 3363 TelephonyStatsLog.write(TelephonyStatsLog.SIM_SPECIFIC_SETTINGS_RESTORED, 3364 TelephonyProtoEnums.SIM_RESTORE_RESULT_SUCCESS, 3365 restoreCase, bestRestoreMatch.getMatchingCriteriaForLogging()); 3366 newlyRestoredSubIds.add(currSubIdFromDb); 3367 } else { 3368 TelephonyStatsLog.write(TelephonyStatsLog.SIM_SPECIFIC_SETTINGS_RESTORED, 3369 TelephonyProtoEnums.SIM_RESTORE_RESULT_NONE_MATCH, 3370 restoreCase, bestRestoreMatch.getMatchingCriteriaForLogging()); 3371 } 3372 } else { 3373 log("No matching SIM in backup data. SIM-specific settings not restored."); 3374 TelephonyStatsLog.write(TelephonyStatsLog.SIM_SPECIFIC_SETTINGS_RESTORED, 3375 TelephonyProtoEnums.SIM_RESTORE_RESULT_ZERO_SIM_IN_BACKUP, 3376 restoreCase, TelephonyProtoEnums.SIM_RESTORE_MATCHING_CRITERIA_NONE); 3377 } 3378 } 3379 3380 // Update the internal file with subIds that we just restored. 3381 previouslyRestoredSubIdsList.addAll(newlyRestoredSubIds); 3382 backedUpDataBundle.putIntArray( 3383 KEY_PREVIOUSLY_RESTORED_SUB_IDS, 3384 previouslyRestoredSubIdsList.stream().mapToInt(i -> i).toArray()); 3385 try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { 3386 backedUpDataBundle.writeToStream(outputStream); 3387 writeSimSettingsToInternalStorage(outputStream.toByteArray()); 3388 } catch (IOException e) { 3389 loge("Not able to convert SimInfoDB to byte array. Not storing which subIds were " 3390 + "restored"); 3391 } 3392 } 3393 3394 private static class SimRestoreMatch { 3395 3396 private Set<Integer> matches = new ArraySet<>(); 3397 private int subId; 3398 private ContentValues contentValues; 3399 private int matchingCriteria; 3400 private int matchScore; 3401 3402 private static final int ICCID_MATCH = 1; 3403 private static final int PHONE_NUMBER_MATCH = 2; 3404 private static final int CARRIER_ID_MATCH = 3; 3405 SimRestoreMatch(String iccIdFromDb, int carrierIdFromDb, String phoneNumberFromDb, String isoCountryCodeFromDb, List<String> wfcRestoreBlockedCountries, PersistableBundle backedUpSimInfoEntry, int backupDataFormatVersion)3406 public SimRestoreMatch(String iccIdFromDb, int carrierIdFromDb, 3407 String phoneNumberFromDb, String isoCountryCodeFromDb, 3408 List<String> wfcRestoreBlockedCountries, 3409 PersistableBundle backedUpSimInfoEntry, int backupDataFormatVersion) { 3410 subId = backedUpSimInfoEntry.getInt( 3411 Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID, 3412 DEFAULT_INT_COLUMN_VALUE); 3413 String iccIdFromBackup = backedUpSimInfoEntry.getString(Telephony.SimInfo.COLUMN_ICC_ID, 3414 ""); 3415 String phoneNumberFromBackup = backedUpSimInfoEntry.getString( 3416 Telephony.SimInfo.COLUMN_NUMBER, ""); 3417 int carrierIdFromBackup = backedUpSimInfoEntry.getInt( 3418 Telephony.SimInfo.COLUMN_CARRIER_ID, 3419 TelephonyManager.UNKNOWN_CARRIER_ID); 3420 3421 3422 // find all matching fields 3423 if (iccIdFromDb != null && iccIdFromDb.equals(iccIdFromBackup) 3424 && !iccIdFromBackup.isEmpty()) { 3425 matches.add(ICCID_MATCH); 3426 } 3427 if (carrierIdFromDb == carrierIdFromBackup 3428 && carrierIdFromBackup != TelephonyManager.UNKNOWN_CARRIER_ID) { 3429 matches.add(CARRIER_ID_MATCH); 3430 } 3431 if (phoneNumberFromDb != null && phoneNumberFromDb.equals(phoneNumberFromBackup) 3432 && !phoneNumberFromBackup.isEmpty()) { 3433 matches.add(PHONE_NUMBER_MATCH); 3434 } 3435 3436 contentValues = convertBackedUpDataToContentValues( 3437 backedUpSimInfoEntry, backupDataFormatVersion, isoCountryCodeFromDb, 3438 wfcRestoreBlockedCountries); 3439 matchScore = calculateMatchScore(); 3440 matchingCriteria = calculateMatchingCriteria(); 3441 } 3442 getSubId()3443 public int getSubId() { 3444 return subId; 3445 } 3446 getContentValues()3447 public ContentValues getContentValues() { 3448 return contentValues; 3449 } 3450 getMatchScore()3451 public int getMatchScore() { 3452 return matchScore; 3453 } 3454 calculateMatchScore()3455 private int calculateMatchScore() { 3456 int score = 0; 3457 3458 if (matches.contains(ICCID_MATCH)) { 3459 score += 100; 3460 } 3461 if (matches.contains(CARRIER_ID_MATCH)) { 3462 if (matches.contains(PHONE_NUMBER_MATCH)) { 3463 score += 10; 3464 } else { 3465 score += 1; 3466 } 3467 } 3468 3469 return score; 3470 } 3471 getMatchingCriteriaForLogging()3472 public int getMatchingCriteriaForLogging() { 3473 return matchingCriteria; 3474 } 3475 calculateMatchingCriteria()3476 private int calculateMatchingCriteria() { 3477 if (matches.contains(ICCID_MATCH)) { 3478 return TelephonyProtoEnums.SIM_RESTORE_MATCHING_CRITERIA_ICCID; 3479 } 3480 if (matches.contains(CARRIER_ID_MATCH)) { 3481 if (matches.contains(PHONE_NUMBER_MATCH)) { 3482 return TelephonyProtoEnums 3483 .SIM_RESTORE_MATCHING_CRITERIA_CARRIER_ID_AND_PHONE_NUMBER; 3484 } else { 3485 return TelephonyProtoEnums.SIM_RESTORE_MATCHING_CRITERIA_CARRIER_ID_ONLY; 3486 } 3487 } 3488 return TelephonyProtoEnums.SIM_RESTORE_MATCHING_CRITERIA_NONE; 3489 } 3490 convertBackedUpDataToContentValues( PersistableBundle backedUpSimInfoEntry, int backupDataFormatVersion, String isoCountryCodeFromDb, List<String> wfcRestoreBlockedCountries)3491 private ContentValues convertBackedUpDataToContentValues( 3492 PersistableBundle backedUpSimInfoEntry, int backupDataFormatVersion, 3493 String isoCountryCodeFromDb, 3494 List<String> wfcRestoreBlockedCountries) { 3495 if (DATABASE_VERSION != 51 << 16) { 3496 throw new AssertionError("The database schema has been updated which might make " 3497 + "the format of #BACKED_UP_SIM_SPECIFIC_SETTINGS_FILE outdated. Make sure to " 3498 + "1) review whether any of the columns in #SIM_INFO_COLUMNS_TO_BACKUP have " 3499 + "been migrated or deleted, 2) add the new column name into one of those " 3500 + "maps, 3) add migration code in this method as necessary, and 4) update the " 3501 + "version check in this if statement."); 3502 } 3503 ContentValues contentValues = new ContentValues(); 3504 // Don't restore anything if restoring from a newer version of the current database. 3505 if (backupDataFormatVersion > DATABASE_VERSION) { 3506 return null; 3507 } 3508 3509 /* Any migration logic should be placed under this comment block. 3510 * ex: 3511 * if (backupDataFormatVersion >= 48 << 19) { 3512 * contentValues.put(NEW_COLUMN_NAME_2, 3513 * backedUpSimInfoEntry.getInt( OLD_COLUMN_NAME, DEFAULT_INT_COLUMN_VALUE)); 3514 * ... 3515 * } else if (backupDataFormatVersion >= 48 << 17) { 3516 * contentValues.put(NEW_COLUMN_NAME_1, 3517 * backedUpSimInfoEntry.getInt(OLD_COLUMN_NAME, DEFAULT_INT_COLUMN_VALUE)); 3518 * ... 3519 * } else { 3520 * // The values from the first format of backup ever available. 3521 * contentValues.put(Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED, 3522 * backedUpSimInfoEntry.getInt( 3523 * Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED, 3524 * DEFAULT_INT_COLUMN_VALUE)); 3525 * contentValues.put(Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED, 3526 * backedUpSimInfoEntry.getString( 3527 * Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED, "")); 3528 * contentValues.put(Telephony.SimInfo.COLUMN_VT_IMS_ENABLED, 3529 * backedUpSimInfoEntry.getString(Telephony.SimInfo.COLUMN_VT_IMS_ENABLED, 3530 * "")); 3531 * ... 3532 * } 3533 * 3534 * Also make sure to add necessary removal of sensitive settings in 3535 * polishContentValues(ContentValues contentValues). 3536 */ 3537 if (backupDataFormatVersion >= 51 << 16) { 3538 contentValues.put(Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS, 3539 backedUpSimInfoEntry.getString( 3540 Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS, 3541 DEFAULT_STRING_COLUMN_VALUE)); 3542 } 3543 if (backupDataFormatVersion >= 50 << 16) { 3544 contentValues.put(Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING, 3545 backedUpSimInfoEntry.getInt( 3546 Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING, 3547 DEFAULT_INT_COLUMN_VALUE)); 3548 } 3549 contentValues.put(Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED, 3550 backedUpSimInfoEntry.getInt( 3551 Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED, 3552 DEFAULT_INT_COLUMN_VALUE)); 3553 contentValues.put(Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED, 3554 backedUpSimInfoEntry.getInt( 3555 Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED, 3556 DEFAULT_INT_COLUMN_VALUE)); 3557 contentValues.put(Telephony.SimInfo.COLUMN_VT_IMS_ENABLED, 3558 backedUpSimInfoEntry.getInt( 3559 Telephony.SimInfo.COLUMN_VT_IMS_ENABLED, 3560 DEFAULT_INT_COLUMN_VALUE)); 3561 if (isoCountryCodeFromDb != null 3562 && !wfcRestoreBlockedCountries 3563 .contains(isoCountryCodeFromDb.toLowerCase())) { 3564 // Don't restore COLUMN_WFC_IMS_ENABLED if the sim is from one of the countries that 3565 // requires WFC entitlement. 3566 contentValues.put(Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED, 3567 backedUpSimInfoEntry.getInt( 3568 Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED, 3569 DEFAULT_INT_COLUMN_VALUE)); 3570 } 3571 contentValues.put(Telephony.SimInfo.COLUMN_WFC_IMS_MODE, 3572 backedUpSimInfoEntry.getInt( 3573 Telephony.SimInfo.COLUMN_WFC_IMS_MODE, 3574 DEFAULT_INT_COLUMN_VALUE)); 3575 contentValues.put(Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE, 3576 backedUpSimInfoEntry.getInt( 3577 Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE, 3578 DEFAULT_INT_COLUMN_VALUE)); 3579 3580 return polishContentValues(contentValues); 3581 } 3582 polishContentValues(ContentValues contentValues)3583 private ContentValues polishContentValues(ContentValues contentValues) { 3584 /* Remove any values that weren't found in the backup file. These were set to defaults 3585 in #convertBackedUpDataToContentValues(). */ 3586 for (Map.Entry<String, Integer> column : SIM_INFO_COLUMNS_TO_BACKUP.entrySet()) { 3587 String columnName = column.getKey(); 3588 3589 if (!contentValues.containsKey(columnName)) { 3590 continue; 3591 } 3592 3593 int columnType = column.getValue(); 3594 if (columnType == Cursor.FIELD_TYPE_INTEGER 3595 && DEFAULT_INT_COLUMN_VALUE == contentValues.getAsInteger(columnName)) { 3596 contentValues.remove(columnName); 3597 } else if (columnType == Cursor.FIELD_TYPE_STRING && contentValues 3598 .getAsString(columnName).equals(DEFAULT_STRING_COLUMN_VALUE)) { 3599 contentValues.remove(columnName); 3600 } 3601 } 3602 3603 if (matches.contains(ICCID_MATCH)) { 3604 return contentValues; 3605 } else if (matches.contains(CARRIER_ID_MATCH)) { 3606 if (!matches.contains(PHONE_NUMBER_MATCH)) { 3607 // Low confidence match should not restore sensitive configs. 3608 if (contentValues.containsKey(Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED)) { 3609 contentValues.remove(Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED); 3610 } 3611 } 3612 return contentValues; 3613 } 3614 return null; 3615 } 3616 3617 } 3618 3619 /** 3620 * Retrieves data from all columns in SimInfoDB of backup/restore interest. 3621 * 3622 * @return data of interest from SimInfoDB as a byte array. 3623 */ getSimSpecificDataToBackUp()3624 private byte[] getSimSpecificDataToBackUp() { 3625 String[] projection = SIM_INFO_COLUMNS_TO_BACKUP.keySet() 3626 .toArray(new String[SIM_INFO_COLUMNS_TO_BACKUP.size()]); 3627 3628 try (Cursor cursor = query(SubscriptionManager.CONTENT_URI, projection, null, null, null); 3629 ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { 3630 PersistableBundle topLevelBundle = new PersistableBundle(); 3631 topLevelBundle.putInt(KEY_BACKUP_DATA_FORMAT_VERSION, DATABASE_VERSION); 3632 for (int rowNum = 0; cursor != null && cursor.moveToNext(); rowNum++) { 3633 PersistableBundle rowBundle = convertSimInfoDbEntryToPersistableBundle(cursor); 3634 topLevelBundle.putPersistableBundle(KEY_SIMINFO_DB_ROW_PREFIX + rowNum, rowBundle); 3635 } 3636 topLevelBundle.writeToStream(outputStream); 3637 return outputStream.toByteArray(); 3638 } catch (IOException e) { 3639 loge("Not able to convert SimInfoDB to byte array. Returning empty byte array"); 3640 return new byte[0]; 3641 } 3642 } 3643 convertSimInfoDbEntryToPersistableBundle(Cursor cursor)3644 private static PersistableBundle convertSimInfoDbEntryToPersistableBundle(Cursor cursor) { 3645 PersistableBundle bundle = new PersistableBundle(); 3646 for (Map.Entry<String, Integer> column : SIM_INFO_COLUMNS_TO_BACKUP.entrySet()) { 3647 String columnName = column.getKey(); 3648 int columnType = column.getValue(); 3649 int columnIndex = cursor.getColumnIndex(columnName); 3650 if (columnType == Cursor.FIELD_TYPE_INTEGER) { 3651 bundle.putInt(columnName, cursor.getInt(columnIndex)); 3652 } else if (columnType == Cursor.FIELD_TYPE_STRING) { 3653 bundle.putString(columnName, cursor.getString(columnIndex)); 3654 } else { 3655 throw new AssertionError("SimInfoDB column to be backed up is not recognized. Make " 3656 + "sure to properly add the desired colum name and value type to " 3657 + "SIM_INFO_COLUMNS_TO_BACKUP."); 3658 } 3659 } 3660 3661 return bundle; 3662 } 3663 3664 @Override query(Uri url, String[] projectionIn, String selection, String[] selectionArgs, String sort)3665 public synchronized Cursor query(Uri url, String[] projectionIn, String selection, 3666 String[] selectionArgs, String sort) { 3667 if (VDBG) log("query: url=" + url + ", projectionIn=" + projectionIn + ", selection=" 3668 + selection + "selectionArgs=" + selectionArgs + ", sort=" + sort); 3669 int subId = SubscriptionManager.getDefaultSubscriptionId(); 3670 String subIdString; 3671 SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 3672 qb.setStrict(true); // a little protection from injection attacks 3673 qb.setTables(CARRIERS_TABLE); 3674 3675 List<String> constraints = new ArrayList<String>(); 3676 3677 int match = s_urlMatcher.match(url); 3678 checkPermissionCompat(match, projectionIn); 3679 switch (match) { 3680 case URL_TELEPHONY_USING_SUBID: { 3681 // The behaves exactly same as URL_SIM_APN_LIST_ID. 3682 subIdString = url.getLastPathSegment(); 3683 try { 3684 subId = Integer.parseInt(subIdString); 3685 } catch (NumberFormatException e) { 3686 loge("NumberFormatException" + e); 3687 return null; 3688 } 3689 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 3690 qb.appendWhereStandalone(IS_NOT_OWNED_BY_DPC); 3691 return getSubscriptionMatchingAPNList(qb, projectionIn, selection, selectionArgs, 3692 sort, subId); 3693 3694 // TODO b/74213956 turn this back on once insertion includes correct sub id 3695 // constraints.add(SUBSCRIPTION_ID + "=" + subIdString); 3696 } 3697 case URL_TELEPHONY: { 3698 constraints.add(IS_NOT_OWNED_BY_DPC); 3699 break; 3700 } 3701 3702 case URL_CURRENT_USING_SUBID: { 3703 subIdString = url.getLastPathSegment(); 3704 try { 3705 subId = Integer.parseInt(subIdString); 3706 } catch (NumberFormatException e) { 3707 loge("NumberFormatException" + e); 3708 return null; 3709 } 3710 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 3711 // TODO b/74213956 turn this back on once insertion includes correct sub id 3712 // constraints.add(SUBSCRIPTION_ID + "=" + subIdString); 3713 } 3714 //intentional fall through from above case 3715 case URL_CURRENT: { 3716 constraints.add("current IS NOT NULL"); 3717 constraints.add(IS_NOT_OWNED_BY_DPC); 3718 // do not ignore the selection since MMS may use it. 3719 //selection = null; 3720 break; 3721 } 3722 3723 case URL_ID: { 3724 constraints.add("_id = " + url.getPathSegments().get(1)); 3725 constraints.add(IS_NOT_OWNED_BY_DPC); 3726 break; 3727 } 3728 3729 case URL_PREFERAPN_USING_SUBID: 3730 case URL_PREFERAPN_NO_UPDATE_USING_SUBID: { 3731 subIdString = url.getLastPathSegment(); 3732 try { 3733 subId = Integer.parseInt(subIdString); 3734 } catch (NumberFormatException e) { 3735 loge("NumberFormatException" + e); 3736 return null; 3737 } 3738 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 3739 // TODO b/74213956 turn this back on once insertion includes correct sub id 3740 // constraints.add(SUBSCRIPTION_ID + "=" + subIdString); 3741 } 3742 //intentional fall through from above case 3743 case URL_PREFERAPN: 3744 case URL_PREFERAPN_NO_UPDATE: { 3745 constraints.add("_id = " + getPreferredApnId(subId, true)); 3746 break; 3747 } 3748 3749 case URL_PREFERAPNSET_USING_SUBID: { 3750 subIdString = url.getLastPathSegment(); 3751 try { 3752 subId = Integer.parseInt(subIdString); 3753 } catch (NumberFormatException e) { 3754 loge("NumberFormatException" + e); 3755 return null; 3756 } 3757 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 3758 // TODO b/74213956 turn this back on once insertion includes correct sub id 3759 // constraints.add(SUBSCRIPTION_ID + "=" + subIdString); 3760 } 3761 // intentional fall through from above case 3762 case URL_PREFERAPNSET: { 3763 final int set = getPreferredApnSetId(subId); 3764 if (set == NO_APN_SET_ID) { 3765 return null; 3766 } 3767 constraints.add(APN_SET_ID + "=" + set); 3768 qb.appendWhere(TextUtils.join(" AND ", constraints)); 3769 return getSubscriptionMatchingAPNList(qb, projectionIn, selection, selectionArgs, 3770 sort, subId); 3771 } 3772 3773 case URL_DPC: { 3774 ensureCallingFromSystemOrPhoneUid("URL_DPC called from non SYSTEM_UID."); 3775 // DPC query only returns DPC records. 3776 constraints.add(IS_OWNED_BY_DPC); 3777 break; 3778 } 3779 3780 case URL_FILTERED_ID: 3781 case URL_FILTERED_USING_SUBID: { 3782 String idString = url.getLastPathSegment(); 3783 if (match == URL_FILTERED_ID) { 3784 constraints.add("_id = " + idString); 3785 } else { 3786 try { 3787 subId = Integer.parseInt(idString); 3788 // TODO b/74213956 turn this back on once insertion includes correct sub id 3789 // constraints.add(SUBSCRIPTION_ID + "=" + subIdString); 3790 } catch (NumberFormatException e) { 3791 loge("NumberFormatException" + e); 3792 return null; 3793 } 3794 } 3795 } 3796 //intentional fall through from above case 3797 case URL_FILTERED: { 3798 if (isManagedApnEnforced()) { 3799 // If enforced, return DPC records only. 3800 constraints.add(IS_OWNED_BY_DPC); 3801 } else { 3802 // Otherwise return non-DPC records only. 3803 constraints.add(IS_NOT_OWNED_BY_DPC); 3804 } 3805 break; 3806 } 3807 3808 case URL_ENFORCE_MANAGED: { 3809 ensureCallingFromSystemOrPhoneUid( 3810 "URL_ENFORCE_MANAGED called from non SYSTEM_UID."); 3811 MatrixCursor cursor = new MatrixCursor(new String[]{ENFORCED_KEY}); 3812 cursor.addRow(new Object[]{isManagedApnEnforced() ? 1 : 0}); 3813 return cursor; 3814 } 3815 3816 case URL_SIMINFO: { 3817 qb.setTables(SIMINFO_TABLE); 3818 break; 3819 } 3820 case URL_SIM_APN_LIST_ID: { 3821 subIdString = url.getLastPathSegment(); 3822 try { 3823 subId = Integer.parseInt(subIdString); 3824 } catch (NumberFormatException e) { 3825 loge("NumberFormatException" + e); 3826 return null; 3827 } 3828 } 3829 //intentional fall through from above case 3830 case URL_SIM_APN_LIST: { 3831 qb.appendWhere(IS_NOT_OWNED_BY_DPC); 3832 return getSubscriptionMatchingAPNList(qb, projectionIn, selection, selectionArgs, 3833 sort, subId); 3834 } 3835 3836 case URL_SIM_APN_LIST_FILTERED_ID: { 3837 subIdString = url.getLastPathSegment(); 3838 try { 3839 subId = Integer.parseInt(subIdString); 3840 } catch (NumberFormatException e) { 3841 loge("NumberFormatException" + e); 3842 return null; 3843 } 3844 } 3845 //intentional fall through from above case 3846 case URL_SIM_APN_LIST_FILTERED: { 3847 if (isManagedApnEnforced()) { 3848 // If enforced, return DPC records only. 3849 qb.appendWhereStandalone(IS_OWNED_BY_DPC); 3850 } else { 3851 // Otherwise return non-DPC records only. 3852 qb.appendWhereStandalone(IS_NOT_OWNED_BY_DPC); 3853 } 3854 return getSubscriptionMatchingAPNList(qb, projectionIn, selection, selectionArgs, 3855 sort, subId); 3856 } 3857 3858 default: { 3859 return null; 3860 } 3861 } 3862 3863 // appendWhere doesn't add ANDs so we do it ourselves 3864 if (constraints.size() > 0) { 3865 qb.appendWhere(TextUtils.join(" AND ", constraints)); 3866 } 3867 3868 SQLiteDatabase db = getReadableDatabase(); 3869 Cursor ret = null; 3870 try { 3871 // Exclude entries marked deleted 3872 if (CARRIERS_TABLE.equals(qb.getTables())) { 3873 if (TextUtils.isEmpty(selection)) { 3874 selection = ""; 3875 } else { 3876 selection += " and "; 3877 } 3878 selection += IS_NOT_USER_DELETED + " and " + 3879 IS_NOT_USER_DELETED_BUT_PRESENT_IN_XML + " and " + 3880 IS_NOT_CARRIER_DELETED + " and " + 3881 IS_NOT_CARRIER_DELETED_BUT_PRESENT_IN_XML; 3882 if (VDBG) log("query: selection modified to " + selection); 3883 } 3884 ret = qb.query(db, projectionIn, selection, selectionArgs, null, null, sort); 3885 } catch (SQLException e) { 3886 loge("got exception when querying: " + e); 3887 } 3888 if (ret != null) 3889 ret.setNotificationUri(getContext().getContentResolver(), url); 3890 return ret; 3891 } 3892 3893 /** 3894 * This method syncs PREF_FILE_FULL_APN with the db based on the current preferred apn ids. 3895 */ updatePreferredApns()3896 private void updatePreferredApns() { 3897 SharedPreferences spApn = getContext().getSharedPreferences(PREF_FILE_APN, 3898 Context.MODE_PRIVATE); 3899 3900 Map<String, ?> allPrefApnId = spApn.getAll(); 3901 for (String key : allPrefApnId.keySet()) { 3902 if (key.startsWith(COLUMN_APN_ID)) { 3903 int subId; 3904 try { 3905 subId = Integer.parseInt(key.substring(COLUMN_APN_ID.length())); 3906 } catch (NumberFormatException e) { 3907 loge("updatePreferredApns: NumberFormatException for key=" + key); 3908 continue; 3909 } 3910 long preferredApnId = getPreferredApnId(subId, false); 3911 if (preferredApnId != INVALID_APN_ID) { 3912 setPreferredApn(preferredApnId, subId); 3913 } 3914 } 3915 } 3916 } 3917 3918 /** 3919 * To find the current sim APN. Query APN based on {MCC, MNC, MVNO} and {Carrier_ID}. 3920 * 3921 * There has three steps: 3922 * 1. Query the APN based on { MCC, MNC, MVNO } and if has results jump to step 3, else jump to 3923 * step 2. 3924 * 2. Fallback to query the parent APN that query based on { MCC, MNC }. 3925 * 3. Append the result with the APN that query based on { Carrier_ID } 3926 */ getSubscriptionMatchingAPNList(SQLiteQueryBuilder qb, String[] projectionIn, String selection, String[] selectionArgs, String sort, int subId)3927 private Cursor getSubscriptionMatchingAPNList(SQLiteQueryBuilder qb, String[] projectionIn, 3928 String selection, String[] selectionArgs, String sort, int subId) { 3929 Cursor ret; 3930 Context context = getContext(); 3931 SubscriptionManager subscriptionManager = (SubscriptionManager) context 3932 .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); 3933 if (!subscriptionManager.isActiveSubscriptionId(subId)) { 3934 return null; 3935 } 3936 3937 final TelephonyManager tm = ((TelephonyManager) context 3938 .getSystemService(Context.TELEPHONY_SERVICE)) 3939 .createForSubscriptionId(subId); 3940 SQLiteDatabase db = getReadableDatabase(); 3941 String mccmnc = tm.getSimOperator(); 3942 int carrierId = tm.getSimCarrierId(); 3943 3944 qb.appendWhereStandalone(IS_NOT_USER_DELETED + " and " + 3945 IS_NOT_USER_DELETED_BUT_PRESENT_IN_XML + " and " + 3946 IS_NOT_CARRIER_DELETED + " and " + 3947 IS_NOT_CARRIER_DELETED_BUT_PRESENT_IN_XML); 3948 3949 // For query db one time, append all conditions in one selection and separate results after 3950 // the query is completed. IMSI has special match rule, so just query the MCC / MNC and 3951 // filter the MVNO by ourselves 3952 qb.appendWhereStandalone(NUMERIC + " = '" + mccmnc + "' OR " + 3953 CARRIER_ID + " = '" + carrierId + "'"); 3954 3955 ret = qb.query(db, null, selection, selectionArgs, null, null, sort); 3956 if (ret == null) { 3957 loge("query current APN but cursor is null."); 3958 return null; 3959 } 3960 3961 if (DBG) log("match current APN size: " + ret.getCount()); 3962 3963 String[] columnNames = projectionIn != null ? projectionIn : ret.getColumnNames(); 3964 MatrixCursor currentCursor = new MatrixCursor(columnNames); 3965 MatrixCursor parentCursor = new MatrixCursor(columnNames); 3966 MatrixCursor carrierIdCursor = new MatrixCursor(columnNames); 3967 3968 int numericIndex = ret.getColumnIndex(NUMERIC); 3969 int mvnoIndex = ret.getColumnIndex(MVNO_TYPE); 3970 int mvnoDataIndex = ret.getColumnIndex(MVNO_MATCH_DATA); 3971 int carrierIdIndex = ret.getColumnIndex(CARRIER_ID); 3972 3973 // Separate the result into MatrixCursor 3974 while (ret.moveToNext()) { 3975 List<String> data = new ArrayList<>(); 3976 for (String column : columnNames) { 3977 data.add(ret.getString(ret.getColumnIndex(column))); 3978 } 3979 3980 boolean isCurrentSimOperator; 3981 final long identity = Binder.clearCallingIdentity(); 3982 try { 3983 isCurrentSimOperator = tm.matchesCurrentSimOperator( 3984 ret.getString(numericIndex), 3985 getMvnoTypeIntFromString(ret.getString(mvnoIndex)), 3986 ret.getString(mvnoDataIndex)); 3987 } finally { 3988 Binder.restoreCallingIdentity(identity); 3989 } 3990 3991 boolean isMVNOAPN = !TextUtils.isEmpty(ret.getString(numericIndex)) 3992 && isCurrentSimOperator; 3993 boolean isMNOAPN = !TextUtils.isEmpty(ret.getString(numericIndex)) 3994 && ret.getString(numericIndex).equals(mccmnc) 3995 && TextUtils.isEmpty(ret.getString(mvnoIndex)); 3996 boolean isCarrierIdAPN = !TextUtils.isEmpty(ret.getString(carrierIdIndex)) 3997 && ret.getString(carrierIdIndex).equals(String.valueOf(carrierId)) 3998 && carrierId != TelephonyManager.UNKNOWN_CARRIER_ID; 3999 4000 if (isMVNOAPN) { 4001 // 1. The APN that query based on legacy SIM MCC/MCC and MVNO 4002 currentCursor.addRow(data); 4003 } else if (isMNOAPN) { 4004 // 2. The APN that query based on SIM MCC/MNC 4005 parentCursor.addRow(data); 4006 } else if (isCarrierIdAPN) { 4007 // The APN that query based on carrier Id (not include the MVNO or MNO APN) 4008 carrierIdCursor.addRow(data); 4009 } 4010 } 4011 ret.close(); 4012 4013 MatrixCursor result; 4014 if (currentCursor.getCount() > 0) { 4015 if (DBG) log("match MVNO APN: " + currentCursor.getCount()); 4016 result = currentCursor; 4017 } else if (parentCursor.getCount() > 0) { 4018 if (DBG) log("match MNO APN: " + parentCursor.getCount()); 4019 result = parentCursor; 4020 } else { 4021 if (DBG) log("can't find the MVNO and MNO APN"); 4022 result = new MatrixCursor(columnNames); 4023 } 4024 4025 if (DBG) log("match carrier id APN: " + carrierIdCursor.getCount()); 4026 appendCursorData(result, carrierIdCursor); 4027 return result; 4028 } 4029 appendCursorData(@onNull MatrixCursor from, @NonNull MatrixCursor to)4030 private static void appendCursorData(@NonNull MatrixCursor from, @NonNull MatrixCursor to) { 4031 while (to.moveToNext()) { 4032 List<Object> data = new ArrayList<>(); 4033 for (String column : to.getColumnNames()) { 4034 int index = to.getColumnIndex(column); 4035 switch (to.getType(index)) { 4036 case Cursor.FIELD_TYPE_INTEGER: 4037 data.add(to.getInt(index)); 4038 break; 4039 case Cursor.FIELD_TYPE_FLOAT: 4040 data.add(to.getFloat(index)); 4041 break; 4042 case Cursor.FIELD_TYPE_BLOB: 4043 data.add(to.getBlob(index)); 4044 break; 4045 case Cursor.FIELD_TYPE_STRING: 4046 case Cursor.FIELD_TYPE_NULL: 4047 data.add(to.getString(index)); 4048 break; 4049 } 4050 } 4051 from.addRow(data); 4052 } 4053 } 4054 4055 @Override getType(Uri url)4056 public String getType(Uri url) 4057 { 4058 switch (s_urlMatcher.match(url)) { 4059 case URL_TELEPHONY: 4060 case URL_TELEPHONY_USING_SUBID: 4061 return "vnd.android.cursor.dir/telephony-carrier"; 4062 4063 case URL_ID: 4064 case URL_FILTERED_ID: 4065 case URL_FILTERED_USING_SUBID: 4066 return "vnd.android.cursor.item/telephony-carrier"; 4067 4068 case URL_PREFERAPN_USING_SUBID: 4069 case URL_PREFERAPN_NO_UPDATE_USING_SUBID: 4070 case URL_PREFERAPN: 4071 case URL_PREFERAPN_NO_UPDATE: 4072 case URL_PREFERAPNSET: 4073 case URL_PREFERAPNSET_USING_SUBID: 4074 return "vnd.android.cursor.item/telephony-carrier"; 4075 4076 default: 4077 throw new IllegalArgumentException("Unknown URL " + url); 4078 } 4079 } 4080 4081 /** 4082 * Insert an array of ContentValues and call notifyChange at the end. 4083 */ 4084 @Override bulkInsert(Uri url, ContentValues[] values)4085 public synchronized int bulkInsert(Uri url, ContentValues[] values) { 4086 return unsynchronizedBulkInsert(url, values); 4087 } 4088 4089 /** 4090 * Do a bulk insert while inside a synchronized function. This is typically not safe and should 4091 * only be done when you are sure there will be no conflict. 4092 */ unsynchronizedBulkInsert(Uri url, ContentValues[] values)4093 private int unsynchronizedBulkInsert(Uri url, ContentValues[] values) { 4094 int count = 0; 4095 boolean notify = false; 4096 for (ContentValues value : values) { 4097 Pair<Uri, Boolean> rowAndNotify = insertSingleRow(url, value); 4098 if (rowAndNotify.first != null) { 4099 count++; 4100 } 4101 if (rowAndNotify.second == true) { 4102 notify = true; 4103 } 4104 } 4105 if (notify) { 4106 getContext().getContentResolver().notifyChange(CONTENT_URI, null, 4107 true, UserHandle.USER_ALL); 4108 } 4109 return count; 4110 } 4111 4112 @Override insert(Uri url, ContentValues initialValues)4113 public synchronized Uri insert(Uri url, ContentValues initialValues) { 4114 Pair<Uri, Boolean> rowAndNotify = insertSingleRow(url, initialValues); 4115 if (rowAndNotify.second) { 4116 getContext().getContentResolver().notifyChange(CONTENT_URI, null, 4117 true, UserHandle.USER_ALL); 4118 } 4119 return rowAndNotify.first; 4120 } 4121 4122 /** 4123 * Internal insert function to prevent code duplication for URL_TELEPHONY and URL_DPC. 4124 * 4125 * @param values the value that caller wants to insert 4126 * @return a pair in which the first element refers to the Uri for the row inserted, the second 4127 * element refers to whether sends out nofitication. 4128 */ insertRowWithValue(ContentValues values)4129 private Pair<Uri, Boolean> insertRowWithValue(ContentValues values) { 4130 Uri result = null; 4131 boolean notify = false; 4132 SQLiteDatabase db = getWritableDatabase(); 4133 4134 try { 4135 // Abort on conflict of unique fields and attempt merge 4136 long rowID = db.insertWithOnConflict(CARRIERS_TABLE, null, values, 4137 SQLiteDatabase.CONFLICT_ABORT); 4138 if (rowID >= 0) { 4139 result = ContentUris.withAppendedId(CONTENT_URI, rowID); 4140 notify = true; 4141 } 4142 if (VDBG) log("insert: inserted " + values.toString() + " rowID = " + rowID); 4143 } catch (SQLException e) { 4144 log("insert: exception " + e); 4145 // Insertion failed which could be due to a conflict. Check if that is the case 4146 // and merge the entries 4147 Cursor oldRow = selectConflictingRow(db, CARRIERS_TABLE, values); 4148 if (oldRow != null) { 4149 ContentValues mergedValues = new ContentValues(); 4150 mergeFieldsAndUpdateDb(db, CARRIERS_TABLE, oldRow, values, 4151 mergedValues, false, getContext()); 4152 oldRow.close(); 4153 notify = true; 4154 } 4155 } 4156 return Pair.create(result, notify); 4157 } 4158 insertSingleRow(Uri url, ContentValues initialValues)4159 private Pair<Uri, Boolean> insertSingleRow(Uri url, ContentValues initialValues) { 4160 Uri result = null; 4161 int subId = SubscriptionManager.getDefaultSubscriptionId(); 4162 4163 checkPermission(); 4164 syncBearerBitmaskAndNetworkTypeBitmask(initialValues); 4165 4166 boolean notify = false; 4167 SQLiteDatabase db = getWritableDatabase(); 4168 int match = s_urlMatcher.match(url); 4169 switch (match) 4170 { 4171 case URL_TELEPHONY_USING_SUBID: 4172 { 4173 String subIdString = url.getLastPathSegment(); 4174 try { 4175 subId = Integer.parseInt(subIdString); 4176 } catch (NumberFormatException e) { 4177 loge("NumberFormatException" + e); 4178 return Pair.create(result, notify); 4179 } 4180 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 4181 } 4182 //intentional fall through from above case 4183 4184 case URL_TELEPHONY: 4185 { 4186 ContentValues values; 4187 if (initialValues != null) { 4188 values = new ContentValues(initialValues); 4189 } else { 4190 values = new ContentValues(); 4191 } 4192 4193 values = setDefaultValue(values); 4194 if (!values.containsKey(EDITED_STATUS)) { 4195 values.put(EDITED_STATUS, CARRIER_EDITED); 4196 } 4197 // Owned_by should be others if inserted via general uri. 4198 values.put(OWNED_BY, OWNED_BY_OTHERS); 4199 4200 Pair<Uri, Boolean> ret = insertRowWithValue(values); 4201 result = ret.first; 4202 notify = ret.second; 4203 break; 4204 } 4205 4206 case URL_CURRENT_USING_SUBID: 4207 { 4208 String subIdString = url.getLastPathSegment(); 4209 try { 4210 subId = Integer.parseInt(subIdString); 4211 } catch (NumberFormatException e) { 4212 loge("NumberFormatException" + e); 4213 return Pair.create(result, notify); 4214 } 4215 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 4216 // FIXME use subId in the query 4217 } 4218 //intentional fall through from above case 4219 4220 case URL_CURRENT: 4221 { 4222 // zero out the previous operator 4223 db.update(CARRIERS_TABLE, s_currentNullMap, CURRENT + "!=0", null); 4224 4225 String numeric = initialValues.getAsString(NUMERIC); 4226 int updated = db.update(CARRIERS_TABLE, s_currentSetMap, 4227 NUMERIC + " = '" + numeric + "'", null); 4228 4229 if (updated > 0) 4230 { 4231 if (VDBG) log("Setting numeric '" + numeric + "' to be the current operator"); 4232 } 4233 else 4234 { 4235 loge("Failed setting numeric '" + numeric + "' to the current operator"); 4236 } 4237 break; 4238 } 4239 4240 case URL_PREFERAPN_USING_SUBID: 4241 case URL_PREFERAPN_NO_UPDATE_USING_SUBID: 4242 { 4243 String subIdString = url.getLastPathSegment(); 4244 try { 4245 subId = Integer.parseInt(subIdString); 4246 } catch (NumberFormatException e) { 4247 loge("NumberFormatException" + e); 4248 return Pair.create(result, notify); 4249 } 4250 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 4251 } 4252 //intentional fall through from above case 4253 4254 case URL_PREFERAPN: 4255 case URL_PREFERAPN_NO_UPDATE: 4256 { 4257 if (initialValues != null) { 4258 if(initialValues.containsKey(COLUMN_APN_ID)) { 4259 setPreferredApnId(initialValues.getAsLong(COLUMN_APN_ID), subId, true); 4260 notify = true; 4261 } 4262 } 4263 break; 4264 } 4265 4266 case URL_DPC: { 4267 ensureCallingFromSystemOrPhoneUid("URL_DPC called from non SYSTEM_UID."); 4268 4269 ContentValues values; 4270 if (initialValues != null) { 4271 values = new ContentValues(initialValues); 4272 } else { 4273 values = new ContentValues(); 4274 } 4275 4276 // Owned_by should be DPC if inserted via URL_DPC. 4277 values.put(OWNED_BY, OWNED_BY_DPC); 4278 // DPC records should not be user editable. 4279 values.put(USER_EDITABLE, false); 4280 4281 final long rowID = db.insertWithOnConflict(CARRIERS_TABLE, null, values, 4282 SQLiteDatabase.CONFLICT_IGNORE); 4283 if (rowID >= 0) { 4284 result = ContentUris.withAppendedId(CONTENT_URI, rowID); 4285 notify = true; 4286 } 4287 if (VDBG) log("insert: inserted " + values.toString() + " rowID = " + rowID); 4288 4289 break; 4290 } 4291 4292 case URL_SIMINFO: { 4293 long id = db.insert(SIMINFO_TABLE, null, initialValues); 4294 result = ContentUris.withAppendedId(Telephony.SimInfo.CONTENT_URI, id); 4295 break; 4296 } 4297 } 4298 4299 return Pair.create(result, notify); 4300 } 4301 4302 @Override delete(Uri url, String where, String[] whereArgs)4303 public synchronized int delete(Uri url, String where, String[] whereArgs) { 4304 int count = 0; 4305 int subId = SubscriptionManager.getDefaultSubscriptionId(); 4306 String userOrCarrierEdited = ") and (" + 4307 IS_USER_EDITED + " or " + 4308 IS_CARRIER_EDITED + ")"; 4309 String notUserOrCarrierEdited = ") and (" + 4310 IS_NOT_USER_EDITED + " and " + 4311 IS_NOT_CARRIER_EDITED + ")"; 4312 String unedited = ") and " + IS_UNEDITED; 4313 ContentValues cv = new ContentValues(); 4314 cv.put(EDITED_STATUS, USER_DELETED); 4315 4316 checkPermission(); 4317 4318 SQLiteDatabase db = getWritableDatabase(); 4319 int match = s_urlMatcher.match(url); 4320 switch (match) 4321 { 4322 case URL_DELETE: 4323 { 4324 // Delete preferred APN for all subIds 4325 deletePreferredApnId(getContext()); 4326 // Delete unedited entries 4327 count = db.delete(CARRIERS_TABLE, "(" + where + unedited + " and " + 4328 IS_NOT_OWNED_BY_DPC, whereArgs); 4329 break; 4330 } 4331 4332 case URL_TELEPHONY_USING_SUBID: 4333 { 4334 String subIdString = url.getLastPathSegment(); 4335 try { 4336 subId = Integer.parseInt(subIdString); 4337 } catch (NumberFormatException e) { 4338 loge("NumberFormatException" + e); 4339 throw new IllegalArgumentException("Invalid subId " + url); 4340 } 4341 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 4342 // FIXME use subId in query 4343 } 4344 //intentional fall through from above case 4345 4346 case URL_TELEPHONY: 4347 { 4348 // Delete user/carrier edited entries 4349 count = db.delete(CARRIERS_TABLE, "(" + where + userOrCarrierEdited 4350 + " and " + IS_NOT_OWNED_BY_DPC, whereArgs); 4351 // Otherwise mark as user deleted instead of deleting 4352 count += db.update(CARRIERS_TABLE, cv, "(" + where + 4353 notUserOrCarrierEdited + " and " + IS_NOT_OWNED_BY_DPC, whereArgs); 4354 break; 4355 } 4356 4357 case URL_CURRENT_USING_SUBID: { 4358 String subIdString = url.getLastPathSegment(); 4359 try { 4360 subId = Integer.parseInt(subIdString); 4361 } catch (NumberFormatException e) { 4362 loge("NumberFormatException" + e); 4363 throw new IllegalArgumentException("Invalid subId " + url); 4364 } 4365 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 4366 // FIXME use subId in query 4367 } 4368 //intentional fall through from above case 4369 4370 case URL_CURRENT: 4371 { 4372 // Delete user/carrier edited entries 4373 count = db.delete(CARRIERS_TABLE, "(" + where + userOrCarrierEdited 4374 + " and " + IS_NOT_OWNED_BY_DPC, whereArgs); 4375 // Otherwise mark as user deleted instead of deleting 4376 count += db.update(CARRIERS_TABLE, cv, "(" + where + 4377 notUserOrCarrierEdited + " and " + IS_NOT_OWNED_BY_DPC, whereArgs); 4378 break; 4379 } 4380 4381 case URL_ID: 4382 { 4383 // Delete user/carrier edited entries 4384 count = db.delete(CARRIERS_TABLE, 4385 "(" + _ID + "=?" + userOrCarrierEdited + 4386 " and " + IS_NOT_OWNED_BY_DPC, 4387 new String[] { url.getLastPathSegment() }); 4388 // Otherwise mark as user deleted instead of deleting 4389 count += db.update(CARRIERS_TABLE, cv, 4390 "(" + _ID + "=?" + notUserOrCarrierEdited + 4391 " and " + IS_NOT_OWNED_BY_DPC, 4392 new String[]{url.getLastPathSegment() }); 4393 break; 4394 } 4395 4396 case URL_RESTOREAPN_USING_SUBID: { 4397 String subIdString = url.getLastPathSegment(); 4398 try { 4399 subId = Integer.parseInt(subIdString); 4400 } catch (NumberFormatException e) { 4401 loge("NumberFormatException" + e); 4402 throw new IllegalArgumentException("Invalid subId " + url); 4403 } 4404 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 4405 } 4406 // intentional fall through from above case 4407 4408 case URL_RESTOREAPN: { 4409 count = 1; 4410 restoreDefaultAPN(subId); 4411 getContext().getContentResolver().notifyChange( 4412 Uri.withAppendedPath(CONTENT_URI, "restore/subId/" + subId), null, 4413 true, UserHandle.USER_ALL); 4414 break; 4415 } 4416 4417 case URL_PREFERAPN_USING_SUBID: 4418 case URL_PREFERAPN_NO_UPDATE_USING_SUBID: { 4419 String subIdString = url.getLastPathSegment(); 4420 try { 4421 subId = Integer.parseInt(subIdString); 4422 } catch (NumberFormatException e) { 4423 loge("NumberFormatException" + e); 4424 throw new IllegalArgumentException("Invalid subId " + url); 4425 } 4426 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 4427 } 4428 //intentional fall through from above case 4429 4430 case URL_PREFERAPN: 4431 case URL_PREFERAPN_NO_UPDATE: 4432 { 4433 setPreferredApnId((long)INVALID_APN_ID, subId, true); 4434 if ((match == URL_PREFERAPN) || (match == URL_PREFERAPN_USING_SUBID)) count = 1; 4435 break; 4436 } 4437 4438 case URL_DPC_ID: { 4439 ensureCallingFromSystemOrPhoneUid("URL_DPC_ID called from non SYSTEM_UID."); 4440 4441 // Only delete if owned by DPC. 4442 count = db.delete(CARRIERS_TABLE, "(" + _ID + "=?)" + " and " + IS_OWNED_BY_DPC, 4443 new String[] { url.getLastPathSegment() }); 4444 break; 4445 } 4446 4447 case URL_SIMINFO: { 4448 count = db.delete(SIMINFO_TABLE, where, whereArgs); 4449 break; 4450 } 4451 4452 case URL_UPDATE_DB: { 4453 updateApnDb(); 4454 count = 1; 4455 break; 4456 } 4457 4458 default: { 4459 throw new UnsupportedOperationException("Cannot delete that URL: " + url); 4460 } 4461 } 4462 4463 if (count > 0) { 4464 getContext().getContentResolver().notifyChange(CONTENT_URI, null, 4465 true, UserHandle.USER_ALL); 4466 } 4467 4468 return count; 4469 } 4470 4471 @Override update(Uri url, ContentValues values, String where, String[] whereArgs)4472 public synchronized int update(Uri url, ContentValues values, String where, String[] whereArgs) 4473 { 4474 int count = 0; 4475 int uriType = URL_UNKNOWN; 4476 int subId = SubscriptionManager.getDefaultSubscriptionId(); 4477 4478 checkPermission(); 4479 syncBearerBitmaskAndNetworkTypeBitmask(values); 4480 4481 SQLiteDatabase db = getWritableDatabase(); 4482 int match = s_urlMatcher.match(url); 4483 switch (match) 4484 { 4485 case URL_TELEPHONY_USING_SUBID: 4486 { 4487 String subIdString = url.getLastPathSegment(); 4488 try { 4489 subId = Integer.parseInt(subIdString); 4490 } catch (NumberFormatException e) { 4491 loge("NumberFormatException" + e); 4492 throw new IllegalArgumentException("Invalid subId " + url); 4493 } 4494 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 4495 //FIXME use subId in the query 4496 } 4497 //intentional fall through from above case 4498 4499 case URL_TELEPHONY: 4500 { 4501 if (!values.containsKey(EDITED_STATUS)) { 4502 values.put(EDITED_STATUS, CARRIER_EDITED); 4503 } 4504 4505 // Replace on conflict so that if same APN is present in db with edited 4506 // as UNEDITED or USER/CARRIER_DELETED, it is replaced with 4507 // edited USER/CARRIER_EDITED 4508 count = db.updateWithOnConflict(CARRIERS_TABLE, values, where + 4509 " and " + IS_NOT_OWNED_BY_DPC, whereArgs, 4510 SQLiteDatabase.CONFLICT_REPLACE); 4511 break; 4512 } 4513 4514 case URL_CURRENT_USING_SUBID: 4515 { 4516 String subIdString = url.getLastPathSegment(); 4517 try { 4518 subId = Integer.parseInt(subIdString); 4519 } catch (NumberFormatException e) { 4520 loge("NumberFormatException" + e); 4521 throw new IllegalArgumentException("Invalid subId " + url); 4522 } 4523 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 4524 //FIXME use subId in the query 4525 } 4526 //intentional fall through from above case 4527 4528 case URL_CURRENT: 4529 { 4530 if (!values.containsKey(EDITED_STATUS)) { 4531 values.put(EDITED_STATUS, CARRIER_EDITED); 4532 } 4533 // Replace on conflict so that if same APN is present in db with edited 4534 // as UNEDITED or USER/CARRIER_DELETED, it is replaced with 4535 // edited USER/CARRIER_EDITED 4536 count = db.updateWithOnConflict(CARRIERS_TABLE, values, where + 4537 " and " + IS_NOT_OWNED_BY_DPC, 4538 whereArgs, SQLiteDatabase.CONFLICT_REPLACE); 4539 break; 4540 } 4541 4542 case URL_ID: 4543 { 4544 String rowID = url.getLastPathSegment(); 4545 if (where != null || whereArgs != null) { 4546 throw new UnsupportedOperationException( 4547 "Cannot update URL " + url + " with a where clause"); 4548 } 4549 if (!values.containsKey(EDITED_STATUS)) { 4550 values.put(EDITED_STATUS, CARRIER_EDITED); 4551 } 4552 4553 try { 4554 count = db.updateWithOnConflict(CARRIERS_TABLE, values, _ID + "=?" + " and " + 4555 IS_NOT_OWNED_BY_DPC, new String[] { rowID }, 4556 SQLiteDatabase.CONFLICT_ABORT); 4557 } catch (SQLException e) { 4558 // Update failed which could be due to a conflict. Check if that is 4559 // the case and merge the entries 4560 log("update: exception " + e); 4561 Cursor oldRow = selectConflictingRow(db, CARRIERS_TABLE, values); 4562 if (oldRow != null) { 4563 ContentValues mergedValues = new ContentValues(); 4564 mergeFieldsAndUpdateDb(db, CARRIERS_TABLE, oldRow, values, 4565 mergedValues, false, getContext()); 4566 oldRow.close(); 4567 db.delete(CARRIERS_TABLE, _ID + "=?" + " and " + IS_NOT_OWNED_BY_DPC, 4568 new String[] { rowID }); 4569 } 4570 } 4571 break; 4572 } 4573 4574 case URL_PREFERAPN_USING_SUBID: 4575 case URL_PREFERAPN_NO_UPDATE_USING_SUBID: 4576 { 4577 String subIdString = url.getLastPathSegment(); 4578 try { 4579 subId = Integer.parseInt(subIdString); 4580 } catch (NumberFormatException e) { 4581 loge("NumberFormatException" + e); 4582 throw new IllegalArgumentException("Invalid subId " + url); 4583 } 4584 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 4585 } 4586 4587 case URL_PREFERAPN: 4588 case URL_PREFERAPN_NO_UPDATE: 4589 { 4590 if (values != null) { 4591 if (values.containsKey(COLUMN_APN_ID)) { 4592 setPreferredApnId(values.getAsLong(COLUMN_APN_ID), subId, true); 4593 if ((match == URL_PREFERAPN) || 4594 (match == URL_PREFERAPN_USING_SUBID)) { 4595 count = 1; 4596 } 4597 } 4598 } 4599 break; 4600 } 4601 4602 case URL_DPC_ID: 4603 { 4604 ensureCallingFromSystemOrPhoneUid("URL_DPC_ID called from non SYSTEM_UID."); 4605 4606 if (where != null || whereArgs != null) { 4607 throw new UnsupportedOperationException( 4608 "Cannot update URL " + url + " with a where clause"); 4609 } 4610 count = db.updateWithOnConflict(CARRIERS_TABLE, values, 4611 _ID + "=?" + " and " + IS_OWNED_BY_DPC, 4612 new String[] { url.getLastPathSegment() }, SQLiteDatabase.CONFLICT_IGNORE); 4613 break; 4614 } 4615 4616 case URL_ENFORCE_MANAGED: { 4617 ensureCallingFromSystemOrPhoneUid( 4618 "URL_ENFORCE_MANAGED called from non SYSTEM_UID."); 4619 if (values != null) { 4620 if (values.containsKey(ENFORCED_KEY)) { 4621 setManagedApnEnforced(values.getAsBoolean(ENFORCED_KEY)); 4622 count = 1; 4623 } 4624 } 4625 break; 4626 } 4627 4628 case URL_SIMINFO_USING_SUBID: 4629 String subIdString = url.getLastPathSegment(); 4630 try { 4631 subId = Integer.parseInt(subIdString); 4632 } catch (NumberFormatException e) { 4633 loge("NumberFormatException" + e); 4634 throw new IllegalArgumentException("Invalid subId " + url); 4635 } 4636 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 4637 if (where != null || whereArgs != null) { 4638 throw new UnsupportedOperationException( 4639 "Cannot update URL " + url + " with a where clause"); 4640 } 4641 count = db.update(SIMINFO_TABLE, values, _ID + "=?", 4642 new String[] { subIdString}); 4643 uriType = URL_SIMINFO_USING_SUBID; 4644 break; 4645 4646 case URL_SIMINFO: { 4647 count = db.update(SIMINFO_TABLE, values, where, whereArgs); 4648 uriType = URL_SIMINFO; 4649 break; 4650 } 4651 4652 case URL_SIMINFO_SUW_RESTORE: 4653 count = db.update(SIMINFO_TABLE, values, where, whereArgs); 4654 uriType = URL_SIMINFO_SUW_RESTORE; 4655 break; 4656 4657 case URL_SIMINFO_SIM_INSERTED_RESTORE: 4658 count = db.update(SIMINFO_TABLE, values, where, whereArgs); 4659 break; 4660 4661 default: { 4662 throw new UnsupportedOperationException("Cannot update that URL: " + url); 4663 } 4664 } 4665 4666 // if APNs (CARRIERS_TABLE) have been updated, some of them may be preferred APN for 4667 // different subs. So update the APN field values saved in SharedPref for all subIds. 4668 switch (match) { 4669 case URL_TELEPHONY_USING_SUBID: 4670 case URL_TELEPHONY: 4671 case URL_CURRENT_USING_SUBID: 4672 case URL_CURRENT: 4673 case URL_ID: 4674 case URL_DPC_ID: 4675 updatePreferredApns(); 4676 break; 4677 } 4678 4679 if (count > 0) { 4680 boolean usingSubId = false; 4681 switch (uriType) { 4682 case URL_SIMINFO_SIM_INSERTED_RESTORE: 4683 break; 4684 case URL_SIMINFO_SUW_RESTORE: 4685 getContext().getContentResolver().notifyChange( 4686 SubscriptionManager.SIM_INFO_SUW_RESTORE_CONTENT_URI, null); 4687 // intentional fall through from above case 4688 case URL_SIMINFO_USING_SUBID: 4689 usingSubId = true; 4690 // intentional fall through from above case 4691 case URL_SIMINFO: 4692 // skip notifying descendant URLs to avoid unneccessary wake up. 4693 // If not set, any change to SIMINFO will notify observers which listens to 4694 // specific field of SIMINFO. 4695 getContext().getContentResolver().notifyChange( 4696 Telephony.SimInfo.CONTENT_URI, null, 4697 ContentResolver.NOTIFY_SYNC_TO_NETWORK 4698 | ContentResolver.NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS, 4699 UserHandle.USER_ALL); 4700 // notify observers on specific user settings changes. 4701 if (values.containsKey(Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED)) { 4702 getContext().getContentResolver().notifyChange( 4703 getNotifyContentUri(SubscriptionManager.WFC_ENABLED_CONTENT_URI, 4704 usingSubId, subId), null, true, UserHandle.USER_ALL); 4705 } 4706 if (values.containsKey(Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED)) { 4707 getContext().getContentResolver().notifyChange( 4708 getNotifyContentUri(SubscriptionManager 4709 .ADVANCED_CALLING_ENABLED_CONTENT_URI, 4710 usingSubId, subId), null, true, UserHandle.USER_ALL); 4711 } 4712 if (values.containsKey(Telephony.SimInfo.COLUMN_VT_IMS_ENABLED)) { 4713 getContext().getContentResolver().notifyChange( 4714 getNotifyContentUri(SubscriptionManager.VT_ENABLED_CONTENT_URI, 4715 usingSubId, subId), null, true, UserHandle.USER_ALL); 4716 } 4717 if (values.containsKey(Telephony.SimInfo.COLUMN_WFC_IMS_MODE)) { 4718 getContext().getContentResolver().notifyChange( 4719 getNotifyContentUri(SubscriptionManager.WFC_MODE_CONTENT_URI, 4720 usingSubId, subId), null, true, UserHandle.USER_ALL); 4721 } 4722 if (values.containsKey(Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE)) { 4723 getContext().getContentResolver().notifyChange(getNotifyContentUri( 4724 SubscriptionManager.WFC_ROAMING_MODE_CONTENT_URI, 4725 usingSubId, subId), null, true, UserHandle.USER_ALL); 4726 } 4727 if (values.containsKey(Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED)) { 4728 getContext().getContentResolver().notifyChange(getNotifyContentUri( 4729 SubscriptionManager.WFC_ROAMING_ENABLED_CONTENT_URI, 4730 usingSubId, subId), null, true, UserHandle.USER_ALL); 4731 } 4732 if (values.containsKey(Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED)) { 4733 getContext().getContentResolver().notifyChange(getNotifyContentUri( 4734 Uri.withAppendedPath(Telephony.SimInfo.CONTENT_URI, 4735 Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED), usingSubId, subId), 4736 null, true, UserHandle.USER_ALL); 4737 } 4738 if (values.containsKey(Telephony.SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED)) { 4739 getContext().getContentResolver().notifyChange(getNotifyContentUri( 4740 Uri.withAppendedPath(Telephony.SimInfo.CONTENT_URI, 4741 Telephony.SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED), 4742 usingSubId, subId), null, true, UserHandle.USER_ALL); 4743 } 4744 if (values.containsKey(Telephony.SimInfo.COLUMN_VOIMS_OPT_IN_STATUS)) { 4745 getContext().getContentResolver().notifyChange(getNotifyContentUri( 4746 Uri.withAppendedPath(Telephony.SimInfo.CONTENT_URI, 4747 Telephony.SimInfo.COLUMN_VOIMS_OPT_IN_STATUS), 4748 usingSubId, subId), null, true, UserHandle.USER_ALL); 4749 } 4750 break; 4751 default: 4752 getContext().getContentResolver().notifyChange( 4753 CONTENT_URI, null, true, UserHandle.USER_ALL); 4754 } 4755 } 4756 4757 return count; 4758 } 4759 getNotifyContentUri(Uri uri, boolean usingSubId, int subId)4760 private static Uri getNotifyContentUri(Uri uri, boolean usingSubId, int subId) { 4761 return (usingSubId) ? Uri.withAppendedPath(uri, "" + subId) : uri; 4762 } 4763 checkPermission()4764 private void checkPermission() { 4765 int status = getContext().checkCallingOrSelfPermission( 4766 "android.permission.WRITE_APN_SETTINGS"); 4767 if (status == PackageManager.PERMISSION_GRANTED) { 4768 return; 4769 } 4770 4771 PackageManager packageManager = getContext().getPackageManager(); 4772 String[] packages = packageManager.getPackagesForUid(Binder.getCallingUid()); 4773 4774 TelephonyManager telephonyManager = 4775 (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE); 4776 final long token = Binder.clearCallingIdentity(); 4777 try { 4778 for (String pkg : packages) { 4779 if (telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(pkg) == 4780 TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { 4781 return; 4782 } 4783 } 4784 } finally { 4785 Binder.restoreCallingIdentity(token); 4786 } 4787 4788 throw new SecurityException("No permission to access APN settings"); 4789 } 4790 4791 /** 4792 * Check permission to query the database based on PlatformCompat settings -- if the compat 4793 * change is enabled, check WRITE_APN_SETTINGS or carrier privs for all queries. Otherwise, 4794 * use the legacy checkQueryPermission method to see if the query should be allowed. 4795 */ checkPermissionCompat(int match, String[] projectionIn)4796 private void checkPermissionCompat(int match, String[] projectionIn) { 4797 boolean useNewBehavior = CompatChanges.isChangeEnabled( 4798 Telephony.Carriers.APN_READING_PERMISSION_CHANGE_ID, 4799 Binder.getCallingUid()); 4800 4801 if (!useNewBehavior) { 4802 log("Using old permission behavior for telephony provider compat"); 4803 checkQueryPermission(match, projectionIn); 4804 } else { 4805 checkPermission(); 4806 } 4807 } 4808 checkQueryPermission(int match, String[] projectionIn)4809 private void checkQueryPermission(int match, String[] projectionIn) { 4810 if (match != URL_SIMINFO) { 4811 if (projectionIn != null) { 4812 for (String column : projectionIn) { 4813 if (TYPE.equals(column) || 4814 MMSC.equals(column) || 4815 MMSPROXY.equals(column) || 4816 MMSPORT.equals(column) || 4817 MVNO_TYPE.equals(column) || 4818 MVNO_MATCH_DATA.equals(column) || 4819 APN.equals(column)) { 4820 // noop 4821 } else { 4822 checkPermission(); 4823 break; 4824 } 4825 } 4826 } else { 4827 // null returns all columns, so need permission check 4828 checkPermission(); 4829 } 4830 } 4831 } 4832 4833 private DatabaseHelper mOpenHelper; 4834 restoreDefaultAPN(int subId)4835 private void restoreDefaultAPN(int subId) { 4836 SQLiteDatabase db = getWritableDatabase(); 4837 TelephonyManager telephonyManager = 4838 (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE); 4839 String where = null; 4840 if (telephonyManager.getPhoneCount() > 1) { 4841 where = getWhereClauseForRestoreDefaultApn(db, subId); 4842 } 4843 if (TextUtils.isEmpty(where)) { 4844 where = IS_NOT_OWNED_BY_DPC; 4845 } 4846 log("restoreDefaultAPN: where: " + where); 4847 4848 try { 4849 db.delete(CARRIERS_TABLE, where, null); 4850 } catch (SQLException e) { 4851 loge("got exception when deleting to restore: " + e); 4852 } 4853 4854 // delete preferred apn ids and preferred apns (both stored in diff SharedPref) for all 4855 // subIds 4856 SharedPreferences spApnId = getContext().getSharedPreferences(PREF_FILE_APN, 4857 Context.MODE_PRIVATE); 4858 SharedPreferences.Editor editorApnId = spApnId.edit(); 4859 editorApnId.clear(); 4860 editorApnId.apply(); 4861 4862 SharedPreferences spApn = getContext().getSharedPreferences(PREF_FILE_FULL_APN, 4863 Context.MODE_PRIVATE); 4864 SharedPreferences.Editor editorApn = spApn.edit(); 4865 editorApn.clear(); 4866 editorApn.apply(); 4867 4868 if (apnSourceServiceExists(getContext())) { 4869 restoreApnsWithService(subId); 4870 } else { 4871 initDatabaseWithDatabaseHelper(db); 4872 } 4873 } 4874 getWhereClauseForRestoreDefaultApn(SQLiteDatabase db, int subId)4875 private String getWhereClauseForRestoreDefaultApn(SQLiteDatabase db, int subId) { 4876 TelephonyManager telephonyManager = 4877 getContext().getSystemService(TelephonyManager.class).createForSubscriptionId(subId); 4878 String simOperator = telephonyManager.getSimOperator(); 4879 Cursor cursor = db.query(CARRIERS_TABLE, new String[] {MVNO_TYPE, MVNO_MATCH_DATA}, 4880 NUMERIC + "='" + simOperator + "'", null, null, null, DEFAULT_SORT_ORDER); 4881 String where = null; 4882 4883 if (cursor != null) { 4884 cursor.moveToFirst(); 4885 while (!cursor.isAfterLast()) { 4886 String mvnoType = cursor.getString(0 /* MVNO_TYPE index */); 4887 String mvnoMatchData = cursor.getString(1 /* MVNO_MATCH_DATA index */); 4888 if (!TextUtils.isEmpty(mvnoType) && !TextUtils.isEmpty(mvnoMatchData) 4889 && telephonyManager.matchesCurrentSimOperator(simOperator, 4890 getMvnoTypeIntFromString(mvnoType), mvnoMatchData)) { 4891 where = NUMERIC + "='" + simOperator + "'" 4892 + " AND " + MVNO_TYPE + "='" + mvnoType + "'" 4893 + " AND " + MVNO_MATCH_DATA + "='" + mvnoMatchData + "'" 4894 + " AND " + IS_NOT_OWNED_BY_DPC; 4895 break; 4896 } 4897 cursor.moveToNext(); 4898 } 4899 cursor.close(); 4900 4901 if (TextUtils.isEmpty(where)) { 4902 where = NUMERIC + "='" + simOperator + "'" 4903 + " AND (" + MVNO_TYPE + "='' OR " + MVNO_MATCH_DATA + "='')" 4904 + " AND " + IS_NOT_OWNED_BY_DPC; 4905 } 4906 } 4907 return where; 4908 } 4909 updateApnDb()4910 private synchronized void updateApnDb() { 4911 if (apnSourceServiceExists(getContext())) { 4912 loge("called updateApnDb when apn source service exists"); 4913 return; 4914 } 4915 4916 if (!needApnDbUpdate()) { 4917 log("Skipping apn db update since apn-conf has not changed."); 4918 return; 4919 } 4920 4921 SQLiteDatabase db = getWritableDatabase(); 4922 4923 // Delete preferred APN for all subIds 4924 deletePreferredApnId(getContext()); 4925 4926 // Delete entries in db 4927 try { 4928 if (VDBG) log("updateApnDb: deleting edited=UNEDITED entries"); 4929 db.delete(CARRIERS_TABLE, IS_UNEDITED + " and " + IS_NOT_OWNED_BY_DPC, null); 4930 } catch (SQLException e) { 4931 loge("got exception when deleting to update: " + e); 4932 } 4933 4934 initDatabaseWithDatabaseHelper(db); 4935 4936 // Notify listeners of DB change since DB has been updated 4937 getContext().getContentResolver().notifyChange( 4938 CONTENT_URI, null, true, UserHandle.USER_ALL); 4939 4940 } 4941 fillInMccMncStringAtCursor(Context context, SQLiteDatabase db, Cursor c)4942 public static void fillInMccMncStringAtCursor(Context context, SQLiteDatabase db, Cursor c) { 4943 int mcc, mnc; 4944 String subId; 4945 try { 4946 mcc = c.getInt(c.getColumnIndexOrThrow(Telephony.SimInfo.COLUMN_MCC)); 4947 mnc = c.getInt(c.getColumnIndexOrThrow(Telephony.SimInfo.COLUMN_MNC)); 4948 subId = c.getString(c.getColumnIndexOrThrow( 4949 Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID)); 4950 } catch (IllegalArgumentException e) { 4951 Log.e(TAG, "Possible database corruption -- some columns not found."); 4952 return; 4953 } 4954 4955 String mccString = String.format(Locale.getDefault(), "%03d", mcc); 4956 String mncString = getBestStringMnc(context, mccString, mnc); 4957 ContentValues cv = new ContentValues(2); 4958 cv.put(Telephony.SimInfo.COLUMN_MCC_STRING, mccString); 4959 cv.put(Telephony.SimInfo.COLUMN_MNC_STRING, mncString); 4960 db.update(SIMINFO_TABLE, cv, 4961 Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=?", 4962 new String[]{subId}); 4963 } 4964 4965 /* 4966 * Find the best string-form mnc by looking up possibilities in the carrier id db. 4967 * Default to the three-digit version if neither/both are valid. 4968 */ getBestStringMnc(Context context, String mcc, int mnc)4969 private static String getBestStringMnc(Context context, String mcc, int mnc) { 4970 if (mnc >= 100 && mnc <= 999) { 4971 return String.valueOf(mnc); 4972 } 4973 String twoDigitMnc = String.format(Locale.getDefault(), "%02d", mnc); 4974 String threeDigitMnc = "0" + twoDigitMnc; 4975 boolean threeDigitNetworkCode = 4976 Arrays.asList(COUNTRY_MCC_WITH_THREE_DIGIT_MNC).contains(mcc); 4977 int twoDigitResult = countMccMncInCarrierList(context, mcc + twoDigitMnc); 4978 int threeDigitResult = countMccMncInCarrierList(context, mcc + threeDigitMnc); 4979 4980 if ((threeDigitResult > twoDigitResult) || 4981 (threeDigitNetworkCode && (twoDigitResult == threeDigitResult))) { 4982 return threeDigitMnc; 4983 } else { 4984 return twoDigitMnc; 4985 } 4986 } 4987 4988 /** 4989 * Check carrier_list how many mcc mnc combo matches there are 4990 */ countMccMncInCarrierList(Context ctx, String mccMncCombo)4991 private static int countMccMncInCarrierList(Context ctx, String mccMncCombo) { 4992 try ( 4993 Cursor mccMncCursor = ctx.getContentResolver().query( 4994 Telephony.CarrierId.All.CONTENT_URI, 4995 /* projection */ null, 4996 /* selection */ Telephony.CarrierId.All.MCCMNC + "=?", 4997 /* selectionArgs */ new String[]{mccMncCombo}, null); 4998 ) 4999 { 5000 return mccMncCursor.getCount(); 5001 } 5002 } 5003 5004 /** 5005 * Sync the bearer bitmask and network type bitmask when inserting and updating. 5006 * Since bearerBitmask is deprecating, map the networkTypeBitmask to bearerBitmask if 5007 * networkTypeBitmask was provided. But if networkTypeBitmask was not provided, map the 5008 * bearerBitmask to networkTypeBitmask. 5009 */ syncBearerBitmaskAndNetworkTypeBitmask(ContentValues values)5010 private static void syncBearerBitmaskAndNetworkTypeBitmask(ContentValues values) { 5011 if (values.containsKey(NETWORK_TYPE_BITMASK)) { 5012 int convertedBitmask = convertNetworkTypeBitmaskToBearerBitmask( 5013 values.getAsInteger(NETWORK_TYPE_BITMASK)); 5014 if (values.containsKey(BEARER_BITMASK) 5015 && convertedBitmask != values.getAsInteger(BEARER_BITMASK)) { 5016 loge("Network type bitmask and bearer bitmask are not compatible."); 5017 } 5018 values.put(BEARER_BITMASK, convertNetworkTypeBitmaskToBearerBitmask( 5019 values.getAsInteger(NETWORK_TYPE_BITMASK))); 5020 } else { 5021 if (values.containsKey(BEARER_BITMASK)) { 5022 int convertedBitmask = convertBearerBitmaskToNetworkTypeBitmask( 5023 values.getAsInteger(BEARER_BITMASK)); 5024 values.put(NETWORK_TYPE_BITMASK, convertedBitmask); 5025 } 5026 } 5027 } 5028 5029 /** 5030 * Log with debug 5031 * 5032 * @param s is string log 5033 */ log(String s)5034 private static void log(String s) { 5035 Log.d(TAG, s); 5036 } 5037 loge(String s)5038 private static void loge(String s) { 5039 Log.e(TAG, s); 5040 } 5041 getMvnoTypeIntFromString(String mvnoType)5042 private static int getMvnoTypeIntFromString(String mvnoType) { 5043 String mvnoTypeString = TextUtils.isEmpty(mvnoType) ? mvnoType : mvnoType.toLowerCase(); 5044 Integer mvnoTypeInt = MVNO_TYPE_STRING_MAP.get(mvnoTypeString); 5045 return mvnoTypeInt == null ? UNSPECIFIED_INT : mvnoTypeInt; 5046 } 5047 getBitmaskFromString(String bearerList)5048 private static int getBitmaskFromString(String bearerList) { 5049 String[] bearers = bearerList.split("\\|"); 5050 int bearerBitmask = 0; 5051 for (String bearer : bearers) { 5052 int bearerInt = 0; 5053 try { 5054 bearerInt = Integer.parseInt(bearer.trim()); 5055 } catch (NumberFormatException nfe) { 5056 return 0; 5057 } 5058 5059 if (bearerInt == 0) { 5060 return 0; 5061 } 5062 bearerBitmask |= getBitmaskForTech(bearerInt); 5063 } 5064 return bearerBitmask; 5065 } 5066 5067 /** 5068 * Transform RIL radio technology value to Network 5069 * type bitmask{@link android.telephony.TelephonyManager.NetworkTypeBitMask}. 5070 * 5071 * @param rat The RIL radio technology. 5072 * @return The network type 5073 * bitmask{@link android.telephony.TelephonyManager.NetworkTypeBitMask}. 5074 */ rilRadioTechnologyToNetworkTypeBitmask(int rat)5075 private static int rilRadioTechnologyToNetworkTypeBitmask(int rat) { 5076 switch (rat) { 5077 case RIL_RADIO_TECHNOLOGY_GPRS: 5078 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_GPRS; 5079 case RIL_RADIO_TECHNOLOGY_EDGE: 5080 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_EDGE; 5081 case RIL_RADIO_TECHNOLOGY_UMTS: 5082 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_UMTS; 5083 case RIL_RADIO_TECHNOLOGY_HSDPA: 5084 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSDPA; 5085 case RIL_RADIO_TECHNOLOGY_HSUPA: 5086 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA; 5087 case RIL_RADIO_TECHNOLOGY_HSPA: 5088 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSPA; 5089 case RIL_RADIO_TECHNOLOGY_IS95A: 5090 case RIL_RADIO_TECHNOLOGY_IS95B: 5091 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_CDMA; 5092 case RIL_RADIO_TECHNOLOGY_1xRTT: 5093 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT; 5094 case RIL_RADIO_TECHNOLOGY_EVDO_0: 5095 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_0; 5096 case RIL_RADIO_TECHNOLOGY_EVDO_A: 5097 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_A; 5098 case RIL_RADIO_TECHNOLOGY_EVDO_B: 5099 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_B; 5100 case RIL_RADIO_TECHNOLOGY_EHRPD: 5101 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_EHRPD; 5102 case RIL_RADIO_TECHNOLOGY_LTE: 5103 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_LTE; 5104 case RIL_RADIO_TECHNOLOGY_HSPAP: 5105 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSPAP; 5106 case RIL_RADIO_TECHNOLOGY_GSM: 5107 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_GSM; 5108 case RIL_RADIO_TECHNOLOGY_TD_SCDMA: 5109 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_TD_SCDMA; 5110 case RIL_RADIO_TECHNOLOGY_IWLAN: 5111 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN; 5112 case RIL_RADIO_TECHNOLOGY_LTE_CA: 5113 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA; 5114 case RIL_RADIO_TECHNOLOGY_NR: 5115 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_NR; 5116 default: 5117 return (int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN; 5118 } 5119 } 5120 5121 /** 5122 * Convert network type bitmask to bearer bitmask. 5123 * 5124 * @param networkTypeBitmask The network type bitmask value 5125 * @return The bearer bitmask value. 5126 */ convertNetworkTypeBitmaskToBearerBitmask(int networkTypeBitmask)5127 private static int convertNetworkTypeBitmaskToBearerBitmask(int networkTypeBitmask) { 5128 if (networkTypeBitmask == 0) { 5129 return 0; 5130 } 5131 5132 int bearerBitmask = 0; 5133 for (int bearerInt = 0; bearerInt < NEXT_RIL_RADIO_TECHNOLOGY; bearerInt++) { 5134 if (bitmaskHasTarget(networkTypeBitmask, 5135 rilRadioTechnologyToNetworkTypeBitmask(bearerInt))) { 5136 bearerBitmask |= getBitmaskForTech(bearerInt); 5137 } 5138 } 5139 return bearerBitmask; 5140 } 5141 5142 /** 5143 * Convert bearer bitmask to network type bitmask. 5144 * 5145 * @param bearerBitmask The bearer bitmask value. 5146 * @return The network type bitmask value. 5147 */ convertBearerBitmaskToNetworkTypeBitmask(int bearerBitmask)5148 private static int convertBearerBitmaskToNetworkTypeBitmask(int bearerBitmask) { 5149 if (bearerBitmask == 0) { 5150 return 0; 5151 } 5152 5153 int networkTypeBitmask = 0; 5154 for (int bearerUnitInt = 0; bearerUnitInt < NEXT_RIL_RADIO_TECHNOLOGY; bearerUnitInt++) { 5155 int bearerUnitBitmask = getBitmaskForTech(bearerUnitInt); 5156 if (bitmaskHasTarget(bearerBitmask, bearerUnitBitmask)) { 5157 networkTypeBitmask |= rilRadioTechnologyToNetworkTypeBitmask(bearerUnitInt); 5158 } 5159 } 5160 return networkTypeBitmask; 5161 } 5162 bitmaskHasTarget(int bearerBitmask, int targetBitmask)5163 private static boolean bitmaskHasTarget(int bearerBitmask, int targetBitmask) { 5164 if (bearerBitmask == 0) { 5165 return true; 5166 } else if (targetBitmask != 0) { 5167 return ((bearerBitmask & targetBitmask) != 0); 5168 } 5169 return false; 5170 } 5171 getBitmaskForTech(int radioTech)5172 private static int getBitmaskForTech(int radioTech) { 5173 if (radioTech >= 1) { 5174 return (1 << (radioTech - 1)); 5175 } 5176 return 0; 5177 } 5178 5179 /** 5180 * Migrate the old Long values{@link Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES} over to 5181 * String{@link Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_ALL_REASON} 5182 * 5183 * @param db The sqlite database to write to 5184 * @param c The {@link Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES} values in the sim info 5185 * table. 5186 */ fillInAllowedNetworkTypesStringAtCursor(SQLiteDatabase db, Cursor c)5187 public static void fillInAllowedNetworkTypesStringAtCursor(SQLiteDatabase db, Cursor c) { 5188 long allowedNetworkTypesReasonCarrier; 5189 String subId; 5190 try { 5191 allowedNetworkTypesReasonCarrier = c.getLong( 5192 c.getColumnIndexOrThrow(Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES)); 5193 subId = c.getString(c.getColumnIndexOrThrow( 5194 Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID)); 5195 } catch (IllegalArgumentException e) { 5196 Log.e(TAG, "Possible database corruption -- some columns not found."); 5197 return; 5198 } 5199 5200 if (allowedNetworkTypesReasonCarrier != -1) { 5201 ContentValues cv = new ContentValues(1); 5202 5203 cv.put(Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS, 5204 "carrier=" + allowedNetworkTypesReasonCarrier); 5205 db.update(SIMINFO_TABLE, cv, 5206 Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=?", 5207 new String[]{subId}); 5208 } 5209 } 5210 } 5211