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