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