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