1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.network.apn;
18 
19 import android.app.Dialog;
20 import android.app.settings.SettingsEnums;
21 import android.content.ContentValues;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.database.Cursor;
25 import android.net.Uri;
26 import android.os.Bundle;
27 import android.os.PersistableBundle;
28 import android.os.UserManager;
29 import android.provider.Telephony;
30 import android.telephony.CarrierConfigManager;
31 import android.telephony.SubscriptionInfo;
32 import android.telephony.SubscriptionManager;
33 import android.telephony.TelephonyManager;
34 import android.text.TextUtils;
35 import android.util.Log;
36 import android.view.KeyEvent;
37 import android.view.Menu;
38 import android.view.MenuInflater;
39 import android.view.MenuItem;
40 import android.view.View;
41 import android.view.View.OnKeyListener;
42 
43 import androidx.annotation.Nullable;
44 import androidx.annotation.VisibleForTesting;
45 import androidx.appcompat.app.AlertDialog;
46 import androidx.preference.EditTextPreference;
47 import androidx.preference.ListPreference;
48 import androidx.preference.MultiSelectListPreference;
49 import androidx.preference.Preference;
50 import androidx.preference.Preference.OnPreferenceChangeListener;
51 import androidx.preference.TwoStatePreference;
52 
53 import com.android.internal.util.ArrayUtils;
54 import com.android.settings.R;
55 import com.android.settings.SettingsPreferenceFragment;
56 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
57 import com.android.settings.network.ProxySubscriptionManager;
58 import com.android.settingslib.utils.ThreadUtils;
59 
60 import java.util.Arrays;
61 import java.util.HashSet;
62 import java.util.List;
63 import java.util.Objects;
64 import java.util.Set;
65 
66 /** Use to edit apn settings. */
67 public class ApnEditor extends SettingsPreferenceFragment
68         implements OnPreferenceChangeListener, OnKeyListener {
69 
70     private static final String TAG = ApnEditor.class.getSimpleName();
71     private static final boolean VDBG = false;   // STOPSHIP if true
72 
73     private static final String KEY_AUTH_TYPE = "auth_type";
74     private static final String KEY_APN_TYPE = "apn_type";
75     private static final String KEY_PROTOCOL = "apn_protocol";
76     private static final String KEY_ROAMING_PROTOCOL = "apn_roaming_protocol";
77     private static final String KEY_CARRIER_ENABLED = "carrier_enabled";
78     private static final String KEY_BEARER_MULTI = "bearer_multi";
79     private static final String KEY_MVNO_TYPE = "mvno_type";
80     private static final String KEY_PASSWORD = "apn_password";
81 
82     @VisibleForTesting
83     static final int MENU_DELETE = Menu.FIRST;
84     private static final int MENU_SAVE = Menu.FIRST + 1;
85     private static final int MENU_CANCEL = Menu.FIRST + 2;
86 
87     @VisibleForTesting
88     static String sNotSet;
89     @VisibleForTesting
90     EditTextPreference mName;
91     @VisibleForTesting
92     EditTextPreference mApn;
93     @VisibleForTesting
94     EditTextPreference mProxy;
95     @VisibleForTesting
96     EditTextPreference mPort;
97     @VisibleForTesting
98     EditTextPreference mUser;
99     @VisibleForTesting
100     EditTextPreference mServer;
101     @VisibleForTesting
102     EditTextPreference mPassword;
103     @VisibleForTesting
104     EditTextPreference mMmsc;
105     @VisibleForTesting
106     EditTextPreference mMcc;
107     @VisibleForTesting
108     EditTextPreference mMnc;
109     @VisibleForTesting
110     EditTextPreference mMmsProxy;
111     @VisibleForTesting
112     EditTextPreference mMmsPort;
113     @VisibleForTesting
114     ListPreference mAuthType;
115     @VisibleForTesting
116     EditTextPreference mApnType;
117     @VisibleForTesting
118     ListPreference mProtocol;
119     @VisibleForTesting
120     ListPreference mRoamingProtocol;
121     @VisibleForTesting
122     TwoStatePreference mCarrierEnabled;
123     @VisibleForTesting
124     MultiSelectListPreference mBearerMulti;
125     @VisibleForTesting
126     ListPreference mMvnoType;
127     @VisibleForTesting
128     EditTextPreference mMvnoMatchData;
129 
130     @VisibleForTesting
131     ApnData mApnData;
132 
133     private String mCurMnc;
134     private String mCurMcc;
135 
136     private boolean mNewApn;
137     private int mSubId;
138     @VisibleForTesting
139     ProxySubscriptionManager mProxySubscriptionMgr;
140     private int mBearerInitialVal = 0;
141     private String mMvnoTypeStr;
142     private String mMvnoMatchDataStr;
143     @VisibleForTesting
144     String[] mReadOnlyApnTypes;
145     @VisibleForTesting
146     String[] mDefaultApnTypes;
147     @VisibleForTesting
148     String mDefaultApnProtocol;
149     @VisibleForTesting
150     String mDefaultApnRoamingProtocol;
151     private String[] mReadOnlyApnFields;
152     private boolean mReadOnlyApn;
153     /**
154      * The APN deletion feature within menu is aligned with the APN adding feature.
155      * Having only one of them could lead to a UX which not that make sense from user's
156      * perspective.
157      *
158      * mIsAddApnAllowed stores the configuration value reading from
159      * CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL to support the presentation
160      * control of the menu options. When false, delete option would be invisible to
161      * the end user.
162      */
163     private boolean mIsAddApnAllowed;
164     private Uri mCarrierUri;
165     private boolean mIsCarrierIdApn;
166 
167     /**
168      * APN types for data connections.  These are usage categories for an APN
169      * entry.  One APN entry may support multiple APN types, eg, a single APN
170      * may service regular internet traffic ("default") as well as MMS-specific
171      * connections.<br/>
172      * APN_TYPE_ALL is a special type to indicate that this APN entry can
173      * service all data connections.
174      */
175     public static final String APN_TYPE_ALL = "*";
176     /** APN type for default data traffic */
177     public static final String APN_TYPE_DEFAULT = "default";
178     /** APN type for MMS traffic */
179     public static final String APN_TYPE_MMS = "mms";
180     /** APN type for SUPL assisted GPS */
181     public static final String APN_TYPE_SUPL = "supl";
182     /** APN type for DUN traffic */
183     public static final String APN_TYPE_DUN = "dun";
184     /** APN type for HiPri traffic */
185     public static final String APN_TYPE_HIPRI = "hipri";
186     /** APN type for FOTA */
187     public static final String APN_TYPE_FOTA = "fota";
188     /** APN type for IMS */
189     public static final String APN_TYPE_IMS = "ims";
190     /** APN type for CBS */
191     public static final String APN_TYPE_CBS = "cbs";
192     /** APN type for IA Initial Attach APN */
193     public static final String APN_TYPE_IA = "ia";
194     /** APN type for Emergency PDN. This is not an IA apn, but is used
195      * for access to carrier services in an emergency call situation. */
196     public static final String APN_TYPE_EMERGENCY = "emergency";
197     /** APN type for Mission Critical Services */
198     public static final String APN_TYPE_MCX = "mcx";
199     /** APN type for XCAP */
200     public static final String APN_TYPE_XCAP = "xcap";
201     /** Array of all APN types */
202     public static final String[] APN_TYPES = {APN_TYPE_DEFAULT,
203             APN_TYPE_MMS,
204             APN_TYPE_SUPL,
205             APN_TYPE_DUN,
206             APN_TYPE_HIPRI,
207             APN_TYPE_FOTA,
208             APN_TYPE_IMS,
209             APN_TYPE_CBS,
210             APN_TYPE_IA,
211             APN_TYPE_EMERGENCY,
212             APN_TYPE_MCX,
213             APN_TYPE_XCAP,
214     };
215 
216     /**
217      * Standard projection for the interesting columns of a normal note.
218      */
219     private static final String[] sProjection = new String[] {
220             Telephony.Carriers._ID,     // 0
221             Telephony.Carriers.NAME,    // 1
222             Telephony.Carriers.APN,     // 2
223             Telephony.Carriers.PROXY,   // 3
224             Telephony.Carriers.PORT,    // 4
225             Telephony.Carriers.USER,    // 5
226             Telephony.Carriers.SERVER,  // 6
227             Telephony.Carriers.PASSWORD, // 7
228             Telephony.Carriers.MMSC, // 8
229             Telephony.Carriers.MCC, // 9
230             Telephony.Carriers.MNC, // 10
231             Telephony.Carriers.NUMERIC, // 11
232             Telephony.Carriers.MMSPROXY, // 12
233             Telephony.Carriers.MMSPORT, // 13
234             Telephony.Carriers.AUTH_TYPE, // 14
235             Telephony.Carriers.TYPE, // 15
236             Telephony.Carriers.PROTOCOL, // 16
237             Telephony.Carriers.CARRIER_ENABLED, // 17
238             Telephony.Carriers.BEARER, // 18
239             Telephony.Carriers.BEARER_BITMASK, // 19
240             Telephony.Carriers.ROAMING_PROTOCOL, // 20
241             Telephony.Carriers.MVNO_TYPE,   // 21
242             Telephony.Carriers.MVNO_MATCH_DATA,  // 22
243             Telephony.Carriers.EDITED_STATUS,   // 23
244             Telephony.Carriers.USER_EDITABLE,   // 24
245             Telephony.Carriers.CARRIER_ID       // 25
246     };
247 
248     private static final int ID_INDEX = 0;
249     @VisibleForTesting
250     static final int NAME_INDEX = 1;
251     @VisibleForTesting
252     static final int APN_INDEX = 2;
253     private static final int PROXY_INDEX = 3;
254     private static final int PORT_INDEX = 4;
255     private static final int USER_INDEX = 5;
256     private static final int SERVER_INDEX = 6;
257     private static final int PASSWORD_INDEX = 7;
258     private static final int MMSC_INDEX = 8;
259     @VisibleForTesting
260     static final int MCC_INDEX = 9;
261     @VisibleForTesting
262     static final int MNC_INDEX = 10;
263     private static final int MMSPROXY_INDEX = 12;
264     private static final int MMSPORT_INDEX = 13;
265     private static final int AUTH_TYPE_INDEX = 14;
266     @VisibleForTesting
267     static final int TYPE_INDEX = 15;
268     @VisibleForTesting
269     static final int PROTOCOL_INDEX = 16;
270     @VisibleForTesting
271     static final int CARRIER_ENABLED_INDEX = 17;
272     private static final int BEARER_INDEX = 18;
273     private static final int BEARER_BITMASK_INDEX = 19;
274     @VisibleForTesting
275     static final int ROAMING_PROTOCOL_INDEX = 20;
276     private static final int MVNO_TYPE_INDEX = 21;
277     private static final int MVNO_MATCH_DATA_INDEX = 22;
278     private static final int EDITED_INDEX = 23;
279     private static final int USER_EDITABLE_INDEX = 24;
280     private static final int CARRIER_ID_INDEX = 25;
281 
282     @Override
onCreate(Bundle icicle)283     public void onCreate(Bundle icicle) {
284         super.onCreate(icicle);
285         if (isUserRestricted()) {
286             Log.e(TAG, "This setting isn't available due to user restriction.");
287             finish();
288             return;
289         }
290 
291         setLifecycleForAllControllers();
292 
293         final Intent intent = getIntent();
294         final String action = intent.getAction();
295         if (TextUtils.isEmpty(action)) {
296             finish();
297             return;
298         }
299         mSubId = intent.getIntExtra(ApnSettings.SUB_ID,
300                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
301 
302         initApnEditorUi();
303         getCarrierCustomizedConfig(getContext());
304 
305         Uri uri = null;
306         if (action.equals(Intent.ACTION_EDIT)) {
307             uri = intent.getData();
308             if (!uri.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) {
309                 Log.e(TAG, "Edit request not for carrier table. Uri: " + uri);
310                 finish();
311                 return;
312             }
313         } else if (action.equals(Intent.ACTION_INSERT)) {
314             mCarrierUri = intent.getData();
315             if (!mCarrierUri.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) {
316                 Log.e(TAG, "Insert request not for carrier table. Uri: " + mCarrierUri);
317                 finish();
318                 return;
319             }
320             mNewApn = true;
321             mMvnoTypeStr = intent.getStringExtra(ApnSettings.MVNO_TYPE);
322             mMvnoMatchDataStr = intent.getStringExtra(ApnSettings.MVNO_MATCH_DATA);
323         } else {
324             finish();
325             return;
326         }
327 
328         // Creates an ApnData to store the apn data temporary, so that we don't need the cursor to
329         // get the apn data. The uri is null if the action is ACTION_INSERT, that mean there is no
330         // record in the database, so create a empty ApnData to represent a empty row of database.
331         if (uri != null) {
332             mApnData = getApnDataFromUri(uri);
333         } else {
334             mApnData = new ApnData(sProjection.length);
335         }
336         final int carrierId = mApnData.getInteger(CARRIER_ID_INDEX,
337                 TelephonyManager.UNKNOWN_CARRIER_ID);
338         mIsCarrierIdApn = (carrierId > TelephonyManager.UNKNOWN_CARRIER_ID);
339 
340         final boolean isUserEdited = mApnData.getInteger(EDITED_INDEX,
341                 Telephony.Carriers.USER_EDITED) == Telephony.Carriers.USER_EDITED;
342 
343         Log.d(TAG, "onCreate: EDITED " + isUserEdited);
344         // if it's not a USER_EDITED apn, check if it's read-only
345         if (!isUserEdited && (mApnData.getInteger(USER_EDITABLE_INDEX, 1) == 0
346                 || apnTypesMatch(mReadOnlyApnTypes, mApnData.getString(TYPE_INDEX)))) {
347             Log.d(TAG, "onCreate: apnTypesMatch; read-only APN");
348             mReadOnlyApn = true;
349             disableAllFields();
350         } else if (!ArrayUtils.isEmpty(mReadOnlyApnFields)) {
351             disableFields(mReadOnlyApnFields);
352         }
353         // Make sure that a user cannot break carrier id APN matching
354         if (mIsCarrierIdApn) {
355             disableFieldsForCarrieridApn();
356         }
357 
358         for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) {
359             getPreferenceScreen().getPreference(i).setOnPreferenceChangeListener(this);
360         }
361     }
362 
363     /**
364      * Enable ProxySubscriptionMgr with Lifecycle support for all controllers
365      * live within this fragment
366      */
setLifecycleForAllControllers()367     private void setLifecycleForAllControllers() {
368         if (mProxySubscriptionMgr == null) {
369             mProxySubscriptionMgr = ProxySubscriptionManager.getInstance(getContext());
370         }
371         mProxySubscriptionMgr.setLifecycle(getLifecycle());
372     }
373 
374     @Override
onViewStateRestored(@ullable Bundle savedInstanceState)375     public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
376         super.onViewStateRestored(savedInstanceState);
377         fillUI(savedInstanceState == null);
378         setCarrierCustomizedConfigToUi();
379     }
380 
381     @VisibleForTesting
formatInteger(String value)382     static String formatInteger(String value) {
383         try {
384             final int intValue = Integer.parseInt(value);
385             return String.format(getCorrectDigitsFormat(value), intValue);
386         } catch (NumberFormatException e) {
387             return value;
388         }
389     }
390 
391     /**
392      * Get the digits format so we preserve leading 0's.
393      * MCCs are 3 digits and MNCs are either 2 or 3.
394      */
getCorrectDigitsFormat(String value)395     static String getCorrectDigitsFormat(String value) {
396         if (value.length() == 2) return "%02d";
397         else return "%03d";
398     }
399 
400 
401     /**
402      * Check if passed in array of APN types indicates all APN types
403      * @param apnTypes array of APN types. "*" indicates all types.
404      * @return true if all apn types are included in the array, false otherwise
405      */
hasAllApns(String[] apnTypes)406     static boolean hasAllApns(String[] apnTypes) {
407         if (ArrayUtils.isEmpty(apnTypes)) {
408             return false;
409         }
410 
411         final List apnList = Arrays.asList(apnTypes);
412         if (apnList.contains(APN_TYPE_ALL)) {
413             Log.d(TAG, "hasAllApns: true because apnList.contains(APN_TYPE_ALL)");
414             return true;
415         }
416         for (String apn : APN_TYPES) {
417             if (!apnList.contains(apn)) {
418                 return false;
419             }
420         }
421 
422         Log.d(TAG, "hasAllApns: true");
423         return true;
424     }
425 
426     /**
427      * Check if APN types overlap.
428      * @param apnTypesArray1 array of APNs. Empty array indicates no APN type; "*" indicates all
429      *                       types
430      * @param apnTypes2 comma separated string of APN types. Empty string represents all types.
431      * @return if any apn type matches return true, otherwise return false
432      */
apnTypesMatch(String[] apnTypesArray1, String apnTypes2)433     private boolean apnTypesMatch(String[] apnTypesArray1, String apnTypes2) {
434         if (ArrayUtils.isEmpty(apnTypesArray1)) {
435             return false;
436         }
437 
438         final String[] apnTypesArray1LowerCase = new String[apnTypesArray1.length];
439         for (int i = 0; i < apnTypesArray1.length; i++) {
440             apnTypesArray1LowerCase[i] = apnTypesArray1[i].toLowerCase();
441         }
442 
443         if (hasAllApns(apnTypesArray1LowerCase) || TextUtils.isEmpty(apnTypes2)) {
444             return true;
445         }
446 
447         final List apnTypesList1 = Arrays.asList(apnTypesArray1LowerCase);
448         final String[] apnTypesArray2 = apnTypes2.split(",");
449 
450         for (String apn : apnTypesArray2) {
451             if (apnTypesList1.contains(apn.trim().toLowerCase())) {
452                 Log.d(TAG, "apnTypesMatch: true because match found for " + apn.trim());
453                 return true;
454             }
455         }
456 
457         Log.d(TAG, "apnTypesMatch: false");
458         return false;
459     }
460 
461     /**
462      * Function to get Preference obj corresponding to an apnField
463      * @param apnField apn field name for which pref is needed
464      * @return Preference obj corresponding to passed in apnField
465      */
getPreferenceFromFieldName(String apnField)466     private Preference getPreferenceFromFieldName(String apnField) {
467         switch (apnField) {
468             case Telephony.Carriers.NAME:
469                 return mName;
470             case Telephony.Carriers.APN:
471                 return mApn;
472             case Telephony.Carriers.PROXY:
473                 return mProxy;
474             case Telephony.Carriers.PORT:
475                 return mPort;
476             case Telephony.Carriers.USER:
477                 return mUser;
478             case Telephony.Carriers.SERVER:
479                 return mServer;
480             case Telephony.Carriers.PASSWORD:
481                 return mPassword;
482             case Telephony.Carriers.MMSPROXY:
483                 return mMmsProxy;
484             case Telephony.Carriers.MMSPORT:
485                 return mMmsPort;
486             case Telephony.Carriers.MMSC:
487                 return mMmsc;
488             case Telephony.Carriers.MCC:
489                 return mMcc;
490             case Telephony.Carriers.MNC:
491                 return mMnc;
492             case Telephony.Carriers.TYPE:
493                 return mApnType;
494             case Telephony.Carriers.AUTH_TYPE:
495                 return mAuthType;
496             case Telephony.Carriers.PROTOCOL:
497                 return mProtocol;
498             case Telephony.Carriers.ROAMING_PROTOCOL:
499                 return mRoamingProtocol;
500             case Telephony.Carriers.CARRIER_ENABLED:
501                 return mCarrierEnabled;
502             case Telephony.Carriers.BEARER:
503             case Telephony.Carriers.BEARER_BITMASK:
504                 return mBearerMulti;
505             case Telephony.Carriers.MVNO_TYPE:
506                 return mMvnoType;
507             case Telephony.Carriers.MVNO_MATCH_DATA:
508                 return mMvnoMatchData;
509         }
510         return null;
511     }
512 
513     /**
514      * Disables given fields so that user cannot modify them
515      *
516      * @param apnFields fields to be disabled
517      */
disableFields(String[] apnFields)518     private void disableFields(String[] apnFields) {
519         for (String apnField : apnFields) {
520             final Preference preference = getPreferenceFromFieldName(apnField);
521             if (preference != null) {
522                 preference.setEnabled(false);
523             }
524         }
525     }
526 
527     /**
528      * Disables all fields so that user cannot modify the APN
529      */
disableAllFields()530     private void disableAllFields() {
531         mName.setEnabled(false);
532         mApn.setEnabled(false);
533         mProxy.setEnabled(false);
534         mPort.setEnabled(false);
535         mUser.setEnabled(false);
536         mServer.setEnabled(false);
537         mPassword.setEnabled(false);
538         mMmsProxy.setEnabled(false);
539         mMmsPort.setEnabled(false);
540         mMmsc.setEnabled(false);
541         mMcc.setEnabled(false);
542         mMnc.setEnabled(false);
543         mApnType.setEnabled(false);
544         mAuthType.setEnabled(false);
545         mProtocol.setEnabled(false);
546         mRoamingProtocol.setEnabled(false);
547         mCarrierEnabled.setEnabled(false);
548         mBearerMulti.setEnabled(false);
549         mMvnoType.setEnabled(false);
550         mMvnoMatchData.setEnabled(false);
551     }
552 
553     /**
554      * Disables fields for a carrier id APN to avoid breaking the match criteria
555      */
disableFieldsForCarrieridApn()556     private void disableFieldsForCarrieridApn() {
557         mMcc.setEnabled(false);
558         mMnc.setEnabled(false);
559         mMvnoType.setEnabled(false);
560         mMvnoMatchData.setEnabled(false);
561     }
562 
563     @Override
getMetricsCategory()564     public int getMetricsCategory() {
565         return SettingsEnums.APN_EDITOR;
566     }
567 
568     @VisibleForTesting
fillUI(boolean firstTime)569     void fillUI(boolean firstTime) {
570         if (firstTime) {
571             // Fill in all the values from the db in both text editor and summary
572             mName.setText(mApnData.getString(NAME_INDEX));
573             mApn.setText(mApnData.getString(APN_INDEX));
574             mProxy.setText(mApnData.getString(PROXY_INDEX));
575             mPort.setText(mApnData.getString(PORT_INDEX));
576             mUser.setText(mApnData.getString(USER_INDEX));
577             mServer.setText(mApnData.getString(SERVER_INDEX));
578             mPassword.setText(mApnData.getString(PASSWORD_INDEX));
579             mMmsProxy.setText(mApnData.getString(MMSPROXY_INDEX));
580             mMmsPort.setText(mApnData.getString(MMSPORT_INDEX));
581             mMmsc.setText(mApnData.getString(MMSC_INDEX));
582             mMcc.setText(mApnData.getString(MCC_INDEX));
583             mMnc.setText(mApnData.getString(MNC_INDEX));
584             mApnType.setText(mApnData.getString(TYPE_INDEX));
585             if (mNewApn) {
586                 final SubscriptionInfo subInfo =
587                         mProxySubscriptionMgr.getAccessibleSubscriptionInfo(mSubId);
588 
589                 // Country code
590                 final String mcc = (subInfo == null) ? null : subInfo.getMccString();
591                 // Network code
592                 final String mnc = (subInfo == null) ? null : subInfo.getMncString();
593 
594                 if (!TextUtils.isEmpty(mcc)) {
595                     // Auto populate MNC and MCC for new entries, based on what SIM reports
596                     mMcc.setText(mcc);
597                     mMnc.setText(mnc);
598                     mCurMnc = mnc;
599                     mCurMcc = mcc;
600                 }
601             }
602             final int authVal = mApnData.getInteger(AUTH_TYPE_INDEX, -1);
603             if (authVal != -1) {
604                 mAuthType.setValueIndex(authVal);
605             } else {
606                 mAuthType.setValue(null);
607             }
608 
609             mProtocol.setValue(mApnData.getString(PROTOCOL_INDEX));
610             mRoamingProtocol.setValue(mApnData.getString(ROAMING_PROTOCOL_INDEX));
611             mCarrierEnabled.setChecked(mApnData.getInteger(CARRIER_ENABLED_INDEX, 1) == 1);
612             mBearerInitialVal = mApnData.getInteger(BEARER_INDEX, 0);
613 
614             final HashSet<String> bearers = new HashSet<String>();
615             int bearerBitmask = mApnData.getInteger(BEARER_BITMASK_INDEX, 0);
616             if (bearerBitmask == 0) {
617                 if (mBearerInitialVal == 0) {
618                     bearers.add("" + 0);
619                 }
620             } else {
621                 int i = 1;
622                 while (bearerBitmask != 0) {
623                     if ((bearerBitmask & 1) == 1) {
624                         bearers.add("" + i);
625                     }
626                     bearerBitmask >>= 1;
627                     i++;
628                 }
629             }
630 
631             if (mBearerInitialVal != 0 && !bearers.contains("" + mBearerInitialVal)) {
632                 // add mBearerInitialVal to bearers
633                 bearers.add("" + mBearerInitialVal);
634             }
635             mBearerMulti.setValues(bearers);
636 
637             mMvnoType.setValue(mApnData.getString(MVNO_TYPE_INDEX));
638             mMvnoMatchData.setEnabled(false);
639             mMvnoMatchData.setText(mApnData.getString(MVNO_MATCH_DATA_INDEX));
640             if (mNewApn && mMvnoTypeStr != null && mMvnoMatchDataStr != null) {
641                 mMvnoType.setValue(mMvnoTypeStr);
642                 mMvnoMatchData.setText(mMvnoMatchDataStr);
643             }
644         }
645 
646         mName.setSummary(checkNull(mName.getText()));
647         mApn.setSummary(checkNull(mApn.getText()));
648         mProxy.setSummary(checkNull(mProxy.getText()));
649         mPort.setSummary(checkNull(mPort.getText()));
650         mUser.setSummary(checkNull(mUser.getText()));
651         mServer.setSummary(checkNull(mServer.getText()));
652         mPassword.setSummary(starify(mPassword.getText()));
653         mMmsProxy.setSummary(checkNull(mMmsProxy.getText()));
654         mMmsPort.setSummary(checkNull(mMmsPort.getText()));
655         mMmsc.setSummary(checkNull(mMmsc.getText()));
656         mMcc.setSummary(formatInteger(checkNull(mMcc.getText())));
657         mMnc.setSummary(formatInteger(checkNull(mMnc.getText())));
658         mApnType.setSummary(checkNull(mApnType.getText()));
659 
660         final String authVal = mAuthType.getValue();
661         if (authVal != null) {
662             final int authValIndex = Integer.parseInt(authVal);
663             mAuthType.setValueIndex(authValIndex);
664 
665             final String[] values = getResources().getStringArray(R.array.apn_auth_entries);
666             mAuthType.setSummary(values[authValIndex]);
667         } else {
668             mAuthType.setSummary(sNotSet);
669         }
670 
671         mProtocol.setSummary(checkNull(protocolDescription(mProtocol.getValue(), mProtocol)));
672         mRoamingProtocol.setSummary(
673                 checkNull(protocolDescription(mRoamingProtocol.getValue(), mRoamingProtocol)));
674         mBearerMulti.setSummary(
675                 checkNull(bearerMultiDescription(mBearerMulti.getValues())));
676         mMvnoType.setSummary(
677                 checkNull(mvnoDescription(mMvnoType.getValue())));
678         mMvnoMatchData.setSummary(checkNullforMvnoValue(mMvnoMatchData.getText()));
679         // allow user to edit carrier_enabled for some APN
680         final boolean ceEditable = getResources().getBoolean(
681                 R.bool.config_allow_edit_carrier_enabled);
682         if (ceEditable) {
683             mCarrierEnabled.setEnabled(true);
684         } else {
685             mCarrierEnabled.setEnabled(false);
686         }
687     }
688 
689     /**
690      * Returns the UI choice (e.g., "IPv4/IPv6") corresponding to the given
691      * raw value of the protocol preference (e.g., "IPV4V6"). If unknown,
692      * return null.
693      */
protocolDescription(String raw, ListPreference protocol)694     private String protocolDescription(String raw, ListPreference protocol) {
695         String uRaw = checkNull(raw).toUpperCase();
696         uRaw = uRaw.equals("IPV4") ? "IP" : uRaw;
697         final int protocolIndex = protocol.findIndexOfValue(uRaw);
698         if (protocolIndex == -1) {
699             return null;
700         } else {
701             final String[] values = getResources().getStringArray(R.array.apn_protocol_entries);
702             try {
703                 return values[protocolIndex];
704             } catch (ArrayIndexOutOfBoundsException e) {
705                 return null;
706             }
707         }
708     }
709 
bearerMultiDescription(Set<String> raw)710     private String bearerMultiDescription(Set<String> raw) {
711         final String[] values = getResources().getStringArray(R.array.bearer_entries);
712         final StringBuilder retVal = new StringBuilder();
713         boolean first = true;
714         for (String bearer : raw) {
715             int bearerIndex = mBearerMulti.findIndexOfValue(bearer);
716             try {
717                 if (first) {
718                     retVal.append(values[bearerIndex]);
719                     first = false;
720                 } else {
721                     retVal.append(", " + values[bearerIndex]);
722                 }
723             } catch (ArrayIndexOutOfBoundsException e) {
724                 // ignore
725             }
726         }
727         final String val = retVal.toString();
728         if (!TextUtils.isEmpty(val)) {
729             return val;
730         }
731         return null;
732     }
733 
mvnoDescription(String newValue)734     private String mvnoDescription(String newValue) {
735         final int mvnoIndex = mMvnoType.findIndexOfValue(newValue);
736         final String oldValue = mMvnoType.getValue();
737 
738         if (mvnoIndex == -1) {
739             return null;
740         } else {
741             final String[] values = getResources().getStringArray(R.array.mvno_type_entries);
742             final boolean mvnoMatchDataUneditable =
743                     mReadOnlyApn || (mReadOnlyApnFields != null
744                             && Arrays.asList(mReadOnlyApnFields)
745                             .contains(Telephony.Carriers.MVNO_MATCH_DATA));
746             mMvnoMatchData.setEnabled(!mvnoMatchDataUneditable && mvnoIndex != 0);
747             if (newValue != null && !newValue.equals(oldValue)) {
748                 if (values[mvnoIndex].equals("SPN")) {
749                     TelephonyManager telephonyManager = (TelephonyManager)
750                             getContext().getSystemService(TelephonyManager.class);
751                     final TelephonyManager telephonyManagerForSubId =
752                             telephonyManager.createForSubscriptionId(mSubId);
753                     if (telephonyManagerForSubId != null) {
754                         telephonyManager = telephonyManagerForSubId;
755                     }
756                     mMvnoMatchData.setText(telephonyManager.getSimOperatorName());
757                 } else if (values[mvnoIndex].equals("IMSI")) {
758                     final SubscriptionInfo subInfo =
759                             mProxySubscriptionMgr.getAccessibleSubscriptionInfo(mSubId);
760                     final String mcc = (subInfo == null) ? "" :
761                             Objects.toString(subInfo.getMccString(), "");
762                     final String mnc = (subInfo == null) ? "" :
763                             Objects.toString(subInfo.getMncString(), "");
764                     mMvnoMatchData.setText(mcc + mnc + "x");
765                 } else if (values[mvnoIndex].equals("GID")) {
766                     TelephonyManager telephonyManager = (TelephonyManager)
767                             getContext().getSystemService(TelephonyManager.class);
768                     final TelephonyManager telephonyManagerForSubId =
769                             telephonyManager.createForSubscriptionId(mSubId);
770                     if (telephonyManagerForSubId != null) {
771                         telephonyManager = telephonyManagerForSubId;
772                     }
773                     mMvnoMatchData.setText(telephonyManager.getGroupIdLevel1());
774                 } else {
775                     // mvno type 'none' case. At this time, mvnoIndex should be 0.
776                     mMvnoMatchData.setText("");
777                 }
778             }
779 
780             try {
781                 return values[mvnoIndex];
782             } catch (ArrayIndexOutOfBoundsException e) {
783                 return null;
784             }
785         }
786     }
787     /**
788      * Callback when preference status changed.
789      */
onPreferenceChange(Preference preference, Object newValue)790     public boolean onPreferenceChange(Preference preference, Object newValue) {
791         String key = preference.getKey();
792         if (KEY_AUTH_TYPE.equals(key)) {
793             try {
794                 final int index = Integer.parseInt((String) newValue);
795                 mAuthType.setValueIndex(index);
796 
797                 final String[] values = getResources().getStringArray(R.array.apn_auth_entries);
798                 mAuthType.setSummary(values[index]);
799             } catch (NumberFormatException e) {
800                 return false;
801             }
802         } else if (KEY_APN_TYPE.equals(key)) {
803             String data = (TextUtils.isEmpty((String) newValue)
804                     && !ArrayUtils.isEmpty(mDefaultApnTypes))
805                     ? getEditableApnType(mDefaultApnTypes) : (String) newValue;
806             if (!TextUtils.isEmpty(data)) {
807                 mApnType.setSummary(data);
808             }
809         } else if (KEY_PROTOCOL.equals(key)) {
810             final String protocol = protocolDescription((String) newValue, mProtocol);
811             if (protocol == null) {
812                 return false;
813             }
814             mProtocol.setSummary(protocol);
815             mProtocol.setValue((String) newValue);
816         } else if (KEY_ROAMING_PROTOCOL.equals(key)) {
817             final String protocol = protocolDescription((String) newValue, mRoamingProtocol);
818             if (protocol == null) {
819                 return false;
820             }
821             mRoamingProtocol.setSummary(protocol);
822             mRoamingProtocol.setValue((String) newValue);
823         } else if (KEY_BEARER_MULTI.equals(key)) {
824             final String bearer = bearerMultiDescription((Set<String>) newValue);
825             if (bearer == null) {
826                 return false;
827             }
828             mBearerMulti.setValues((Set<String>) newValue);
829             mBearerMulti.setSummary(bearer);
830         } else if (KEY_MVNO_TYPE.equals(key)) {
831             final String mvno = mvnoDescription((String) newValue);
832             if (mvno == null) {
833                 return false;
834             }
835             mMvnoType.setValue((String) newValue);
836             mMvnoType.setSummary(mvno);
837             mMvnoMatchData.setSummary(checkNullforMvnoValue(mMvnoMatchData.getText()));
838         } else if (KEY_PASSWORD.equals(key)) {
839             mPassword.setSummary(starify(newValue != null ? String.valueOf(newValue) : ""));
840         } else if (KEY_CARRIER_ENABLED.equals(key)) {
841             // do nothing
842         } else {
843             preference.setSummary(checkNull(newValue != null ? String.valueOf(newValue) : null));
844         }
845         return true;
846     }
847 
848     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)849     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
850         super.onCreateOptionsMenu(menu, inflater);
851         // If it's a new APN, then cancel will delete the new entry in onPause
852         // If APN add is not allowed, delete might lead to issue regarding recovery
853         if (!mNewApn && !mReadOnlyApn && mIsAddApnAllowed) {
854             menu.add(0, MENU_DELETE, 0, R.string.menu_delete)
855                 .setIcon(R.drawable.ic_delete);
856         }
857         if (!mReadOnlyApn) {
858             menu.add(0, MENU_SAVE, 0, R.string.menu_save)
859                 .setIcon(android.R.drawable.ic_menu_save);
860         }
861         menu.add(0, MENU_CANCEL, 0, R.string.menu_cancel)
862             .setIcon(android.R.drawable.ic_menu_close_clear_cancel);
863     }
864 
865     @Override
onOptionsItemSelected(MenuItem item)866     public boolean onOptionsItemSelected(MenuItem item) {
867         switch (item.getItemId()) {
868             case MENU_DELETE:
869                 deleteApn();
870                 finish();
871                 return true;
872             case MENU_SAVE:
873                 if (validateAndSaveApnData()) {
874                     finish();
875                 }
876                 return true;
877             case MENU_CANCEL:
878                 finish();
879                 return true;
880             default:
881                 return super.onOptionsItemSelected(item);
882         }
883     }
884 
885     @Override
onViewCreated(View view, Bundle savedInstanceState)886     public void onViewCreated(View view, Bundle savedInstanceState) {
887         super.onViewCreated(view, savedInstanceState);
888         view.setOnKeyListener(this);
889         view.setFocusableInTouchMode(true);
890         view.requestFocus();
891     }
892 
893     /**
894      * Try to save the apn data when pressed the back button. An error message will be displayed if
895      * the apn data is invalid.
896      *
897      * TODO(b/77339593): Try to keep the same behavior between back button and up navigate button.
898      * We will save the valid apn data to the database when pressed the back button, but discard all
899      * user changed when pressed the up navigate button.
900      */
901     @Override
onKey(View v, int keyCode, KeyEvent event)902     public boolean onKey(View v, int keyCode, KeyEvent event) {
903         if (event.getAction() != KeyEvent.ACTION_DOWN) return false;
904         switch (keyCode) {
905             case KeyEvent.KEYCODE_BACK: {
906                 if (validateAndSaveApnData()) {
907                     finish();
908                 }
909                 return true;
910             }
911         }
912         return false;
913     }
914 
915     /**
916      * Add key, value to {@code cv} and compare the value against the value at index in
917      * {@link #mApnData}.
918      *
919      * <p>
920      * The key, value will not add to {@code cv} if value is null.
921      *
922      * @return true if values are different. {@code assumeDiff} indicates if values can be assumed
923      * different in which case no comparison is needed.
924      */
setStringValueAndCheckIfDiff( ContentValues cv, String key, String value, boolean assumeDiff, int index)925     boolean setStringValueAndCheckIfDiff(
926             ContentValues cv, String key, String value, boolean assumeDiff, int index) {
927         final String valueFromLocalCache = mApnData.getString(index);
928         if (VDBG) {
929             Log.d(TAG, "setStringValueAndCheckIfDiff: assumeDiff: " + assumeDiff
930                     + " key: " + key
931                     + " value: '" + value
932                     + "' valueFromDb: '" + valueFromLocalCache + "'");
933         }
934         final boolean isDiff = assumeDiff
935                 || !((TextUtils.isEmpty(value) && TextUtils.isEmpty(valueFromLocalCache))
936                 || (value != null && value.equals(valueFromLocalCache)));
937 
938         if (isDiff && value != null) {
939             cv.put(key, value);
940         }
941         return isDiff;
942     }
943 
944     /**
945      * Add key, value to {@code cv} and compare the value against the value at index in
946      * {@link #mApnData}.
947      *
948      * @return true if values are different. {@code assumeDiff} indicates if values can be assumed
949      * different in which case no comparison is needed.
950      */
setIntValueAndCheckIfDiff( ContentValues cv, String key, int value, boolean assumeDiff, int index)951     boolean setIntValueAndCheckIfDiff(
952             ContentValues cv, String key, int value, boolean assumeDiff, int index) {
953         final Integer valueFromLocalCache = mApnData.getInteger(index);
954         if (VDBG) {
955             Log.d(TAG, "setIntValueAndCheckIfDiff: assumeDiff: " + assumeDiff
956                     + " key: " + key
957                     + " value: '" + value
958                     + "' valueFromDb: '" + valueFromLocalCache + "'");
959         }
960 
961         final boolean isDiff = assumeDiff || value != valueFromLocalCache;
962         if (isDiff) {
963             cv.put(key, value);
964         }
965         return isDiff;
966     }
967 
968     /**
969      * Validates the apn data and save it to the database if it's valid.
970      *
971      * <p>
972      * A dialog with error message will be displayed if the APN data is invalid.
973      *
974      * @return true if there is no error
975      */
976     @VisibleForTesting
validateAndSaveApnData()977     boolean validateAndSaveApnData() {
978         // Nothing to do if it's a read only APN
979         if (mReadOnlyApn) {
980             return true;
981         }
982 
983         final String name = checkNotSet(mName.getText());
984         final String apn = checkNotSet(mApn.getText());
985         final String mcc = checkNotSet(mMcc.getText());
986         final String mnc = checkNotSet(mMnc.getText());
987 
988         final String errorMsg = validateApnData();
989         if (errorMsg != null) {
990             showError();
991             return false;
992         }
993 
994         final ContentValues values = new ContentValues();
995         // call update() if it's a new APN. If not, check if any field differs from the db value;
996         // if any diff is found update() should be called
997         boolean callUpdate = mNewApn;
998         callUpdate = setStringValueAndCheckIfDiff(values,
999                 Telephony.Carriers.NAME,
1000                 name,
1001                 callUpdate,
1002                 NAME_INDEX);
1003 
1004         callUpdate = setStringValueAndCheckIfDiff(values,
1005                 Telephony.Carriers.APN,
1006                 apn,
1007                 callUpdate,
1008                 APN_INDEX);
1009 
1010         callUpdate = setStringValueAndCheckIfDiff(values,
1011                 Telephony.Carriers.PROXY,
1012                 checkNotSet(mProxy.getText()),
1013                 callUpdate,
1014                 PROXY_INDEX);
1015 
1016         callUpdate = setStringValueAndCheckIfDiff(values,
1017                 Telephony.Carriers.PORT,
1018                 checkNotSet(mPort.getText()),
1019                 callUpdate,
1020                 PORT_INDEX);
1021 
1022         callUpdate = setStringValueAndCheckIfDiff(values,
1023                 Telephony.Carriers.MMSPROXY,
1024                 checkNotSet(mMmsProxy.getText()),
1025                 callUpdate,
1026                 MMSPROXY_INDEX);
1027 
1028         callUpdate = setStringValueAndCheckIfDiff(values,
1029                 Telephony.Carriers.MMSPORT,
1030                 checkNotSet(mMmsPort.getText()),
1031                 callUpdate,
1032                 MMSPORT_INDEX);
1033 
1034         callUpdate = setStringValueAndCheckIfDiff(values,
1035                 Telephony.Carriers.USER,
1036                 checkNotSet(mUser.getText()),
1037                 callUpdate,
1038                 USER_INDEX);
1039 
1040         callUpdate = setStringValueAndCheckIfDiff(values,
1041                 Telephony.Carriers.SERVER,
1042                 checkNotSet(mServer.getText()),
1043                 callUpdate,
1044                 SERVER_INDEX);
1045 
1046         callUpdate = setStringValueAndCheckIfDiff(values,
1047                 Telephony.Carriers.PASSWORD,
1048                 checkNotSet(mPassword.getText()),
1049                 callUpdate,
1050                 PASSWORD_INDEX);
1051 
1052         callUpdate = setStringValueAndCheckIfDiff(values,
1053                 Telephony.Carriers.MMSC,
1054                 checkNotSet(mMmsc.getText()),
1055                 callUpdate,
1056                 MMSC_INDEX);
1057 
1058         final String authVal = mAuthType.getValue();
1059         if (authVal != null) {
1060             callUpdate = setIntValueAndCheckIfDiff(values,
1061                     Telephony.Carriers.AUTH_TYPE,
1062                     Integer.parseInt(authVal),
1063                     callUpdate,
1064                     AUTH_TYPE_INDEX);
1065         }
1066 
1067         callUpdate = setStringValueAndCheckIfDiff(values,
1068                 Telephony.Carriers.PROTOCOL,
1069                 checkNotSet(mProtocol.getValue()),
1070                 callUpdate,
1071                 PROTOCOL_INDEX);
1072 
1073         callUpdate = setStringValueAndCheckIfDiff(values,
1074                 Telephony.Carriers.ROAMING_PROTOCOL,
1075                 checkNotSet(mRoamingProtocol.getValue()),
1076                 callUpdate,
1077                 ROAMING_PROTOCOL_INDEX);
1078 
1079         callUpdate = setStringValueAndCheckIfDiff(values,
1080                 Telephony.Carriers.TYPE,
1081                 checkNotSet(getUserEnteredApnType()),
1082                 callUpdate,
1083                 TYPE_INDEX);
1084 
1085         callUpdate = setStringValueAndCheckIfDiff(values,
1086                 Telephony.Carriers.MCC,
1087                 mcc,
1088                 callUpdate,
1089                 MCC_INDEX);
1090 
1091         callUpdate = setStringValueAndCheckIfDiff(values,
1092                 Telephony.Carriers.MNC,
1093                 mnc,
1094                 callUpdate,
1095                 MNC_INDEX);
1096 
1097         values.put(Telephony.Carriers.NUMERIC, mcc + mnc);
1098 
1099         if (mCurMnc != null && mCurMcc != null) {
1100             if (mCurMnc.equals(mnc) && mCurMcc.equals(mcc)) {
1101                 values.put(Telephony.Carriers.CURRENT, 1);
1102             }
1103         }
1104 
1105         final Set<String> bearerSet = mBearerMulti.getValues();
1106         int bearerBitmask = 0;
1107         for (String bearer : bearerSet) {
1108             if (Integer.parseInt(bearer) == 0) {
1109                 bearerBitmask = 0;
1110                 break;
1111             } else {
1112                 bearerBitmask |= getBitmaskForTech(Integer.parseInt(bearer));
1113             }
1114         }
1115         callUpdate = setIntValueAndCheckIfDiff(values,
1116                 Telephony.Carriers.BEARER_BITMASK,
1117                 bearerBitmask,
1118                 callUpdate,
1119                 BEARER_BITMASK_INDEX);
1120 
1121         int bearerVal;
1122         if (bearerBitmask == 0 || mBearerInitialVal == 0) {
1123             bearerVal = 0;
1124         } else if (bitmaskHasTech(bearerBitmask, mBearerInitialVal)) {
1125             bearerVal = mBearerInitialVal;
1126         } else {
1127             // bearer field was being used but bitmask has changed now and does not include the
1128             // initial bearer value -- setting bearer to 0 but maybe better behavior is to choose a
1129             // random tech from the new bitmask??
1130             bearerVal = 0;
1131         }
1132         callUpdate = setIntValueAndCheckIfDiff(values,
1133                 Telephony.Carriers.BEARER,
1134                 bearerVal,
1135                 callUpdate,
1136                 BEARER_INDEX);
1137 
1138         callUpdate = setStringValueAndCheckIfDiff(values,
1139                 Telephony.Carriers.MVNO_TYPE,
1140                 checkNotSet(mMvnoType.getValue()),
1141                 callUpdate,
1142                 MVNO_TYPE_INDEX);
1143 
1144         callUpdate = setStringValueAndCheckIfDiff(values,
1145                 Telephony.Carriers.MVNO_MATCH_DATA,
1146                 checkNotSet(mMvnoMatchData.getText()),
1147                 callUpdate,
1148                 MVNO_MATCH_DATA_INDEX);
1149 
1150         callUpdate = setIntValueAndCheckIfDiff(values,
1151                 Telephony.Carriers.CARRIER_ENABLED,
1152                 mCarrierEnabled.isChecked() ? 1 : 0,
1153                 callUpdate,
1154                 CARRIER_ENABLED_INDEX);
1155 
1156         values.put(Telephony.Carriers.EDITED_STATUS, Telephony.Carriers.USER_EDITED);
1157 
1158         if (callUpdate) {
1159             final Uri uri = mApnData.getUri() == null ? mCarrierUri : mApnData.getUri();
1160             updateApnDataToDatabase(uri, values);
1161         } else {
1162             if (VDBG) Log.d(TAG, "validateAndSaveApnData: not calling update()");
1163         }
1164 
1165         return true;
1166     }
1167 
updateApnDataToDatabase(Uri uri, ContentValues values)1168     private void updateApnDataToDatabase(Uri uri, ContentValues values) {
1169         ThreadUtils.postOnBackgroundThread(() -> {
1170             if (uri.equals(mCarrierUri)) {
1171                 // Add a new apn to the database
1172                 final Uri newUri = getContentResolver().insert(mCarrierUri, values);
1173                 if (newUri == null) {
1174                     Log.e(TAG, "Can't add a new apn to database " + mCarrierUri);
1175                 }
1176             } else {
1177                 // Update the existing apn
1178                 getContentResolver().update(
1179                         uri, values, null /* where */, null /* selection Args */);
1180             }
1181         });
1182     }
1183 
1184     /**
1185      * Validates whether the apn data is valid.
1186      *
1187      * @return An error message if the apn data is invalid, otherwise return null.
1188      */
1189     @VisibleForTesting
validateApnData()1190     String validateApnData() {
1191         String errorMsg = null;
1192 
1193         final String name = checkNotSet(mName.getText());
1194         final String apn = checkNotSet(mApn.getText());
1195         final String mcc = checkNotSet(mMcc.getText());
1196         final String mnc = checkNotSet(mMnc.getText());
1197         boolean doNotCheckMccMnc = mIsCarrierIdApn && TextUtils.isEmpty(mcc)
1198                 && TextUtils.isEmpty(mnc);
1199         if (TextUtils.isEmpty(name)) {
1200             errorMsg = getResources().getString(R.string.error_name_empty);
1201         } else if (TextUtils.isEmpty(apn)) {
1202             errorMsg = getResources().getString(R.string.error_apn_empty);
1203         } else if (doNotCheckMccMnc) {
1204             Log.d(TAG, "validateApnData: carrier id APN does not have mcc/mnc defined");
1205             // no op, skip mcc mnc null check
1206         } else if (mcc == null || mcc.length() != 3) {
1207             errorMsg = getResources().getString(R.string.error_mcc_not3);
1208         } else if ((mnc == null || (mnc.length() & 0xFFFE) != 2)) {
1209             errorMsg = getResources().getString(R.string.error_mnc_not23);
1210         }
1211 
1212         if (errorMsg == null) {
1213             // if carrier does not allow editing certain apn types, make sure type does not include
1214             // those
1215             if (!ArrayUtils.isEmpty(mReadOnlyApnTypes)
1216                     && apnTypesMatch(mReadOnlyApnTypes, getUserEnteredApnType())) {
1217                 final StringBuilder stringBuilder = new StringBuilder();
1218                 for (String type : mReadOnlyApnTypes) {
1219                     stringBuilder.append(type).append(", ");
1220                     Log.d(TAG, "validateApnData: appending type: " + type);
1221                 }
1222                 // remove last ", "
1223                 if (stringBuilder.length() >= 2) {
1224                     stringBuilder.delete(stringBuilder.length() - 2, stringBuilder.length());
1225                 }
1226                 errorMsg = String.format(getResources().getString(R.string.error_adding_apn_type),
1227                         stringBuilder);
1228             }
1229         }
1230 
1231         return errorMsg;
1232     }
1233 
1234     @VisibleForTesting
showError()1235     void showError() {
1236         ErrorDialog.showError(this);
1237     }
1238 
deleteApn()1239     private void deleteApn() {
1240         if (mApnData.getUri() != null) {
1241             getContentResolver().delete(mApnData.getUri(), null, null);
1242             mApnData = new ApnData(sProjection.length);
1243         }
1244     }
1245 
starify(String value)1246     private String starify(String value) {
1247         if (value == null || value.length() == 0) {
1248             return sNotSet;
1249         } else {
1250             final char[] password = new char[value.length()];
1251             for (int i = 0; i < password.length; i++) {
1252                 password[i] = '*';
1253             }
1254             return new String(password);
1255         }
1256     }
1257 
1258     /**
1259      * Returns {@link #sNotSet} if the given string {@code value} is null or empty. The string
1260      * {@link #sNotSet} typically used as the default display when an entry in the preference is
1261      * null or empty.
1262      */
checkNull(String value)1263     private String checkNull(String value) {
1264         return TextUtils.isEmpty(value) ? sNotSet : value;
1265     }
1266 
1267     /**
1268      * To make traslation be diversity, use another string id for MVNO value.
1269      */
checkNullforMvnoValue(String value)1270     private String checkNullforMvnoValue(String value) {
1271         String notSetForMvnoValue = getResources().getString(R.string.apn_not_set_for_mvno);
1272         return TextUtils.isEmpty(value) ? notSetForMvnoValue : value;
1273     }
1274 
1275     /**
1276      * Returns null if the given string {@code value} equals to {@link #sNotSet}. This method
1277      * should be used when convert a string value from preference to database.
1278      */
checkNotSet(String value)1279     private String checkNotSet(String value) {
1280         return sNotSet.equals(value) ? null : value;
1281     }
1282 
1283     @VisibleForTesting
getUserEnteredApnType()1284     String getUserEnteredApnType() {
1285         // if user has not specified a type, map it to "ALL APN TYPES THAT ARE NOT READ-ONLY"
1286         // but if user enter empty type, map it just for default
1287         String userEnteredApnType = mApnType.getText();
1288         if (userEnteredApnType != null) userEnteredApnType = userEnteredApnType.trim();
1289         if ((TextUtils.isEmpty(userEnteredApnType)
1290                 || APN_TYPE_ALL.equals(userEnteredApnType))) {
1291             userEnteredApnType = getEditableApnType(APN_TYPES);
1292         }
1293         Log.d(TAG, "getUserEnteredApnType: changed apn type to editable apn types: "
1294                 + userEnteredApnType);
1295         return userEnteredApnType;
1296     }
1297 
getEditableApnType(String[] apnTypeList)1298     private String getEditableApnType(String[] apnTypeList) {
1299         final StringBuilder editableApnTypes = new StringBuilder();
1300         final List<String> readOnlyApnTypes = Arrays.asList(mReadOnlyApnTypes);
1301         boolean first = true;
1302         for (String apnType : apnTypeList) {
1303             // add APN type if it is not read-only and is not wild-cardable
1304             if (!readOnlyApnTypes.contains(apnType)
1305                     && !apnType.equals(APN_TYPE_IA)
1306                     && !apnType.equals(APN_TYPE_EMERGENCY)
1307                     && !apnType.equals(APN_TYPE_MCX)
1308                     && !apnType.equals(APN_TYPE_IMS)) {
1309                 if (first) {
1310                     first = false;
1311                 } else {
1312                     editableApnTypes.append(",");
1313                 }
1314                 editableApnTypes.append(apnType);
1315             }
1316         }
1317         return editableApnTypes.toString();
1318     }
1319 
initApnEditorUi()1320     private void initApnEditorUi() {
1321         addPreferencesFromResource(R.xml.apn_editor);
1322 
1323         sNotSet = getResources().getString(R.string.apn_not_set);
1324         mName = (EditTextPreference) findPreference("apn_name");
1325         mApn = (EditTextPreference) findPreference("apn_apn");
1326         mProxy = (EditTextPreference) findPreference("apn_http_proxy");
1327         mPort = (EditTextPreference) findPreference("apn_http_port");
1328         mUser = (EditTextPreference) findPreference("apn_user");
1329         mServer = (EditTextPreference) findPreference("apn_server");
1330         mPassword = (EditTextPreference) findPreference(KEY_PASSWORD);
1331         mMmsProxy = (EditTextPreference) findPreference("apn_mms_proxy");
1332         mMmsPort = (EditTextPreference) findPreference("apn_mms_port");
1333         mMmsc = (EditTextPreference) findPreference("apn_mmsc");
1334         mMcc = (EditTextPreference) findPreference("apn_mcc");
1335         mMnc = (EditTextPreference) findPreference("apn_mnc");
1336         mApnType = (EditTextPreference) findPreference("apn_type");
1337         mAuthType = (ListPreference) findPreference(KEY_AUTH_TYPE);
1338         mProtocol = (ListPreference) findPreference(KEY_PROTOCOL);
1339         mRoamingProtocol = (ListPreference) findPreference(KEY_ROAMING_PROTOCOL);
1340         mCarrierEnabled = (TwoStatePreference) findPreference(KEY_CARRIER_ENABLED);
1341         mBearerMulti = (MultiSelectListPreference) findPreference(KEY_BEARER_MULTI);
1342         mMvnoType = (ListPreference) findPreference(KEY_MVNO_TYPE);
1343         mMvnoMatchData = (EditTextPreference) findPreference("mvno_match_data");
1344     }
1345 
1346     @VisibleForTesting
getCarrierCustomizedConfig(Context context)1347     protected void getCarrierCustomizedConfig(Context context) {
1348         mReadOnlyApn = false;
1349         mReadOnlyApnTypes = null;
1350         mReadOnlyApnFields = null;
1351         mIsAddApnAllowed = true;
1352 
1353         final CarrierConfigManager configManager = (CarrierConfigManager)
1354             context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
1355         if (configManager != null) {
1356             final PersistableBundle b = configManager.getConfigForSubId(mSubId);
1357             if (b != null) {
1358                 mReadOnlyApnTypes = b.getStringArray(
1359                         CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY);
1360                 if (!ArrayUtils.isEmpty(mReadOnlyApnTypes)) {
1361                     Log.d(TAG,
1362                             "onCreate: read only APN type: " + Arrays.toString(mReadOnlyApnTypes));
1363                 }
1364                 mReadOnlyApnFields = b.getStringArray(
1365                         CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY);
1366 
1367                 mDefaultApnTypes = b.getStringArray(
1368                         CarrierConfigManager.KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY);
1369 
1370                 if (!ArrayUtils.isEmpty(mDefaultApnTypes)) {
1371                     Log.d(TAG, "onCreate: default apn types: " + Arrays.toString(mDefaultApnTypes));
1372                 }
1373 
1374                 mDefaultApnProtocol = b.getString(
1375                         CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_PROTOCOL_STRING);
1376                 if (!TextUtils.isEmpty(mDefaultApnProtocol)) {
1377                     Log.d(TAG, "onCreate: default apn protocol: " + mDefaultApnProtocol);
1378                 }
1379 
1380                 mDefaultApnRoamingProtocol = b.getString(
1381                         CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING);
1382                 if (!TextUtils.isEmpty(mDefaultApnRoamingProtocol)) {
1383                     Log.d(TAG, "onCreate: default apn roaming protocol: "
1384                             + mDefaultApnRoamingProtocol);
1385                 }
1386 
1387                 mIsAddApnAllowed = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL);
1388                 if (!mIsAddApnAllowed) {
1389                     Log.d(TAG, "onCreate: not allow to add new APN");
1390                 }
1391             }
1392         }
1393     }
1394 
setCarrierCustomizedConfigToUi()1395     private void setCarrierCustomizedConfigToUi() {
1396         if (TextUtils.isEmpty(mApnType.getText()) && !ArrayUtils.isEmpty(mDefaultApnTypes)) {
1397             String value = getEditableApnType(mDefaultApnTypes);
1398             mApnType.setText(value);
1399             mApnType.setSummary(value);
1400         }
1401 
1402         String protocol = protocolDescription(mDefaultApnProtocol, mProtocol);
1403         if (TextUtils.isEmpty(mProtocol.getValue()) && !TextUtils.isEmpty(protocol)) {
1404             mProtocol.setValue(mDefaultApnProtocol);
1405             mProtocol.setSummary(protocol);
1406         }
1407 
1408         String roamingProtocol = protocolDescription(mDefaultApnRoamingProtocol, mRoamingProtocol);
1409         if (TextUtils.isEmpty(mRoamingProtocol.getValue()) && !TextUtils.isEmpty(roamingProtocol)) {
1410             mRoamingProtocol.setValue(mDefaultApnRoamingProtocol);
1411             mRoamingProtocol.setSummary(roamingProtocol);
1412         }
1413     }
1414 
1415     /**
1416      * Dialog of error message.
1417      */
1418     public static class ErrorDialog extends InstrumentedDialogFragment {
1419         /**
1420          * Show error dialog.
1421          */
showError(ApnEditor editor)1422         public static void showError(ApnEditor editor) {
1423             final ErrorDialog dialog = new ErrorDialog();
1424             dialog.setTargetFragment(editor, 0);
1425             dialog.show(editor.getFragmentManager(), "error");
1426         }
1427 
1428         @Override
onCreateDialog(Bundle savedInstanceState)1429         public Dialog onCreateDialog(Bundle savedInstanceState) {
1430             final String msg = ((ApnEditor) getTargetFragment()).validateApnData();
1431 
1432             return new AlertDialog.Builder(getContext())
1433                     .setTitle(R.string.error_title)
1434                     .setPositiveButton(android.R.string.ok, null)
1435                     .setMessage(msg)
1436                     .create();
1437         }
1438 
1439         @Override
getMetricsCategory()1440         public int getMetricsCategory() {
1441             return SettingsEnums.DIALOG_APN_EDITOR_ERROR;
1442         }
1443     }
1444 
1445     @VisibleForTesting
getApnDataFromUri(Uri uri)1446     ApnData getApnDataFromUri(Uri uri) {
1447         ApnData apnData = null;
1448         try (Cursor cursor = getContentResolver().query(
1449                 uri,
1450                 sProjection,
1451                 null /* selection */,
1452                 null /* selectionArgs */,
1453                 null /* sortOrder */)) {
1454             if (cursor != null && cursor.moveToFirst()) {
1455                 apnData = new ApnData(uri, cursor);
1456             }
1457         }
1458 
1459         if (apnData == null) {
1460             Log.d(TAG, "Can't get apnData from Uri " + uri);
1461         }
1462 
1463         return apnData;
1464     }
1465 
1466     @VisibleForTesting
isUserRestricted()1467     boolean isUserRestricted() {
1468         UserManager userManager = getContext().getSystemService(UserManager.class);
1469         if (userManager == null) {
1470             return false;
1471         }
1472         if (!userManager.isAdminUser()) {
1473             Log.e(TAG, "User is not an admin");
1474             return true;
1475         }
1476         if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) {
1477             Log.e(TAG, "User is not allowed to configure mobile network");
1478             return true;
1479         }
1480         return false;
1481     }
1482 
1483     @VisibleForTesting
1484     static class ApnData {
1485         /**
1486          * The uri correspond to a database row of the apn data. This should be null if the apn
1487          * is not in the database.
1488          */
1489         Uri mUri;
1490 
1491         /** Each element correspond to a column of the database row. */
1492         Object[] mData;
1493 
ApnData(int numberOfField)1494         ApnData(int numberOfField) {
1495             mData = new Object[numberOfField];
1496         }
1497 
ApnData(Uri uri, Cursor cursor)1498         ApnData(Uri uri, Cursor cursor) {
1499             mUri = uri;
1500             mData = new Object[cursor.getColumnCount()];
1501             for (int i = 0; i < mData.length; i++) {
1502                 switch (cursor.getType(i)) {
1503                     case Cursor.FIELD_TYPE_FLOAT:
1504                         mData[i] = cursor.getFloat(i);
1505                         break;
1506                     case Cursor.FIELD_TYPE_INTEGER:
1507                         mData[i] = cursor.getInt(i);
1508                         break;
1509                     case Cursor.FIELD_TYPE_STRING:
1510                         mData[i] = cursor.getString(i);
1511                         break;
1512                     case Cursor.FIELD_TYPE_BLOB:
1513                         mData[i] = cursor.getBlob(i);
1514                         break;
1515                     default:
1516                         mData[i] = null;
1517                 }
1518             }
1519         }
1520 
getUri()1521         Uri getUri() {
1522             return mUri;
1523         }
1524 
setUri(Uri uri)1525         void setUri(Uri uri) {
1526             mUri = uri;
1527         }
1528 
getInteger(int index)1529         Integer getInteger(int index) {
1530             return (Integer) mData[index];
1531         }
1532 
getInteger(int index, Integer defaultValue)1533         Integer getInteger(int index, Integer defaultValue) {
1534             final Integer val = getInteger(index);
1535             return val == null ? defaultValue : val;
1536         }
1537 
getString(int index)1538         String getString(int index) {
1539             return (String) mData[index];
1540         }
1541     }
1542 
getBitmaskForTech(int radioTech)1543     private static int getBitmaskForTech(int radioTech) {
1544         if (radioTech >= 1) {
1545             return (1 << (radioTech - 1));
1546         }
1547         return 0;
1548     }
1549 
bitmaskHasTech(int bearerBitmask, int radioTech)1550     private static boolean bitmaskHasTech(int bearerBitmask, int radioTech) {
1551         if (bearerBitmask == 0) {
1552             return true;
1553         } else if (radioTech >= 1) {
1554             return ((bearerBitmask & (1 << (radioTech - 1))) != 0);
1555         }
1556         return false;
1557     }
1558 }
1559