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