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