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