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