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