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