• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.SwitchPreference;
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     SwitchPreference 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         if (hasAllApns(apnTypesArray1) || TextUtils.isEmpty(apnTypes2)) {
439             return true;
440         }
441 
442         final List apnTypesList1 = Arrays.asList(apnTypesArray1);
443         final String[] apnTypesArray2 = apnTypes2.split(",");
444 
445         for (String apn : apnTypesArray2) {
446             if (apnTypesList1.contains(apn.trim())) {
447                 Log.d(TAG, "apnTypesMatch: true because match found for " + apn.trim());
448                 return true;
449             }
450         }
451 
452         Log.d(TAG, "apnTypesMatch: false");
453         return false;
454     }
455 
456     /**
457      * Function to get Preference obj corresponding to an apnField
458      * @param apnField apn field name for which pref is needed
459      * @return Preference obj corresponding to passed in apnField
460      */
getPreferenceFromFieldName(String apnField)461     private Preference getPreferenceFromFieldName(String apnField) {
462         switch (apnField) {
463             case Telephony.Carriers.NAME:
464                 return mName;
465             case Telephony.Carriers.APN:
466                 return mApn;
467             case Telephony.Carriers.PROXY:
468                 return mProxy;
469             case Telephony.Carriers.PORT:
470                 return mPort;
471             case Telephony.Carriers.USER:
472                 return mUser;
473             case Telephony.Carriers.SERVER:
474                 return mServer;
475             case Telephony.Carriers.PASSWORD:
476                 return mPassword;
477             case Telephony.Carriers.MMSPROXY:
478                 return mMmsProxy;
479             case Telephony.Carriers.MMSPORT:
480                 return mMmsPort;
481             case Telephony.Carriers.MMSC:
482                 return mMmsc;
483             case Telephony.Carriers.MCC:
484                 return mMcc;
485             case Telephony.Carriers.MNC:
486                 return mMnc;
487             case Telephony.Carriers.TYPE:
488                 return mApnType;
489             case Telephony.Carriers.AUTH_TYPE:
490                 return mAuthType;
491             case Telephony.Carriers.PROTOCOL:
492                 return mProtocol;
493             case Telephony.Carriers.ROAMING_PROTOCOL:
494                 return mRoamingProtocol;
495             case Telephony.Carriers.CARRIER_ENABLED:
496                 return mCarrierEnabled;
497             case Telephony.Carriers.BEARER:
498             case Telephony.Carriers.BEARER_BITMASK:
499                 return mBearerMulti;
500             case Telephony.Carriers.MVNO_TYPE:
501                 return mMvnoType;
502             case Telephony.Carriers.MVNO_MATCH_DATA:
503                 return mMvnoMatchData;
504         }
505         return null;
506     }
507 
508     /**
509      * Disables given fields so that user cannot modify them
510      *
511      * @param apnFields fields to be disabled
512      */
disableFields(String[] apnFields)513     private void disableFields(String[] apnFields) {
514         for (String apnField : apnFields) {
515             final Preference preference = getPreferenceFromFieldName(apnField);
516             if (preference != null) {
517                 preference.setEnabled(false);
518             }
519         }
520     }
521 
522     /**
523      * Disables all fields so that user cannot modify the APN
524      */
disableAllFields()525     private void disableAllFields() {
526         mName.setEnabled(false);
527         mApn.setEnabled(false);
528         mProxy.setEnabled(false);
529         mPort.setEnabled(false);
530         mUser.setEnabled(false);
531         mServer.setEnabled(false);
532         mPassword.setEnabled(false);
533         mMmsProxy.setEnabled(false);
534         mMmsPort.setEnabled(false);
535         mMmsc.setEnabled(false);
536         mMcc.setEnabled(false);
537         mMnc.setEnabled(false);
538         mApnType.setEnabled(false);
539         mAuthType.setEnabled(false);
540         mProtocol.setEnabled(false);
541         mRoamingProtocol.setEnabled(false);
542         mCarrierEnabled.setEnabled(false);
543         mBearerMulti.setEnabled(false);
544         mMvnoType.setEnabled(false);
545         mMvnoMatchData.setEnabled(false);
546     }
547 
548     /**
549      * Disables fields for a carrier id APN to avoid breaking the match criteria
550      */
disableFieldsForCarrieridApn()551     private void disableFieldsForCarrieridApn() {
552         mMcc.setEnabled(false);
553         mMnc.setEnabled(false);
554         mMvnoType.setEnabled(false);
555         mMvnoMatchData.setEnabled(false);
556     }
557 
558     @Override
getMetricsCategory()559     public int getMetricsCategory() {
560         return SettingsEnums.APN_EDITOR;
561     }
562 
563     @VisibleForTesting
fillUI(boolean firstTime)564     void fillUI(boolean firstTime) {
565         if (firstTime) {
566             // Fill in all the values from the db in both text editor and summary
567             mName.setText(mApnData.getString(NAME_INDEX));
568             mApn.setText(mApnData.getString(APN_INDEX));
569             mProxy.setText(mApnData.getString(PROXY_INDEX));
570             mPort.setText(mApnData.getString(PORT_INDEX));
571             mUser.setText(mApnData.getString(USER_INDEX));
572             mServer.setText(mApnData.getString(SERVER_INDEX));
573             mPassword.setText(mApnData.getString(PASSWORD_INDEX));
574             mMmsProxy.setText(mApnData.getString(MMSPROXY_INDEX));
575             mMmsPort.setText(mApnData.getString(MMSPORT_INDEX));
576             mMmsc.setText(mApnData.getString(MMSC_INDEX));
577             mMcc.setText(mApnData.getString(MCC_INDEX));
578             mMnc.setText(mApnData.getString(MNC_INDEX));
579             mApnType.setText(mApnData.getString(TYPE_INDEX));
580             if (mNewApn) {
581                 final SubscriptionInfo subInfo =
582                         mProxySubscriptionMgr.getAccessibleSubscriptionInfo(mSubId);
583 
584                 // Country code
585                 final String mcc = (subInfo == null) ? null : subInfo.getMccString();
586                 // Network code
587                 final String mnc = (subInfo == null) ? null : subInfo.getMncString();
588 
589                 if (!TextUtils.isEmpty(mcc)) {
590                     // Auto populate MNC and MCC for new entries, based on what SIM reports
591                     mMcc.setText(mcc);
592                     mMnc.setText(mnc);
593                     mCurMnc = mnc;
594                     mCurMcc = mcc;
595                 }
596             }
597             final int authVal = mApnData.getInteger(AUTH_TYPE_INDEX, -1);
598             if (authVal != -1) {
599                 mAuthType.setValueIndex(authVal);
600             } else {
601                 mAuthType.setValue(null);
602             }
603 
604             mProtocol.setValue(mApnData.getString(PROTOCOL_INDEX));
605             mRoamingProtocol.setValue(mApnData.getString(ROAMING_PROTOCOL_INDEX));
606             mCarrierEnabled.setChecked(mApnData.getInteger(CARRIER_ENABLED_INDEX, 1) == 1);
607             mBearerInitialVal = mApnData.getInteger(BEARER_INDEX, 0);
608 
609             final HashSet<String> bearers = new HashSet<String>();
610             int bearerBitmask = mApnData.getInteger(BEARER_BITMASK_INDEX, 0);
611             if (bearerBitmask == 0) {
612                 if (mBearerInitialVal == 0) {
613                     bearers.add("" + 0);
614                 }
615             } else {
616                 int i = 1;
617                 while (bearerBitmask != 0) {
618                     if ((bearerBitmask & 1) == 1) {
619                         bearers.add("" + i);
620                     }
621                     bearerBitmask >>= 1;
622                     i++;
623                 }
624             }
625 
626             if (mBearerInitialVal != 0 && !bearers.contains("" + mBearerInitialVal)) {
627                 // add mBearerInitialVal to bearers
628                 bearers.add("" + mBearerInitialVal);
629             }
630             mBearerMulti.setValues(bearers);
631 
632             mMvnoType.setValue(mApnData.getString(MVNO_TYPE_INDEX));
633             mMvnoMatchData.setEnabled(false);
634             mMvnoMatchData.setText(mApnData.getString(MVNO_MATCH_DATA_INDEX));
635             if (mNewApn && mMvnoTypeStr != null && mMvnoMatchDataStr != null) {
636                 mMvnoType.setValue(mMvnoTypeStr);
637                 mMvnoMatchData.setText(mMvnoMatchDataStr);
638             }
639         }
640 
641         mName.setSummary(checkNull(mName.getText()));
642         mApn.setSummary(checkNull(mApn.getText()));
643         mProxy.setSummary(checkNull(mProxy.getText()));
644         mPort.setSummary(checkNull(mPort.getText()));
645         mUser.setSummary(checkNull(mUser.getText()));
646         mServer.setSummary(checkNull(mServer.getText()));
647         mPassword.setSummary(starify(mPassword.getText()));
648         mMmsProxy.setSummary(checkNull(mMmsProxy.getText()));
649         mMmsPort.setSummary(checkNull(mMmsPort.getText()));
650         mMmsc.setSummary(checkNull(mMmsc.getText()));
651         mMcc.setSummary(formatInteger(checkNull(mMcc.getText())));
652         mMnc.setSummary(formatInteger(checkNull(mMnc.getText())));
653         mApnType.setSummary(checkNull(mApnType.getText()));
654 
655         final String authVal = mAuthType.getValue();
656         if (authVal != null) {
657             final int authValIndex = Integer.parseInt(authVal);
658             mAuthType.setValueIndex(authValIndex);
659 
660             final String[] values = getResources().getStringArray(R.array.apn_auth_entries);
661             mAuthType.setSummary(values[authValIndex]);
662         } else {
663             mAuthType.setSummary(sNotSet);
664         }
665 
666         mProtocol.setSummary(checkNull(protocolDescription(mProtocol.getValue(), mProtocol)));
667         mRoamingProtocol.setSummary(
668                 checkNull(protocolDescription(mRoamingProtocol.getValue(), mRoamingProtocol)));
669         mBearerMulti.setSummary(
670                 checkNull(bearerMultiDescription(mBearerMulti.getValues())));
671         mMvnoType.setSummary(
672                 checkNull(mvnoDescription(mMvnoType.getValue())));
673         mMvnoMatchData.setSummary(checkNullforMvnoValue(mMvnoMatchData.getText()));
674         // allow user to edit carrier_enabled for some APN
675         final boolean ceEditable = getResources().getBoolean(
676                 R.bool.config_allow_edit_carrier_enabled);
677         if (ceEditable) {
678             mCarrierEnabled.setEnabled(true);
679         } else {
680             mCarrierEnabled.setEnabled(false);
681         }
682     }
683 
684     /**
685      * Returns the UI choice (e.g., "IPv4/IPv6") corresponding to the given
686      * raw value of the protocol preference (e.g., "IPV4V6"). If unknown,
687      * return null.
688      */
protocolDescription(String raw, ListPreference protocol)689     private String protocolDescription(String raw, ListPreference protocol) {
690         String uRaw = checkNull(raw).toUpperCase();
691         uRaw = uRaw.equals("IPV4") ? "IP" : uRaw;
692         final int protocolIndex = protocol.findIndexOfValue(uRaw);
693         if (protocolIndex == -1) {
694             return null;
695         } else {
696             final String[] values = getResources().getStringArray(R.array.apn_protocol_entries);
697             try {
698                 return values[protocolIndex];
699             } catch (ArrayIndexOutOfBoundsException e) {
700                 return null;
701             }
702         }
703     }
704 
bearerMultiDescription(Set<String> raw)705     private String bearerMultiDescription(Set<String> raw) {
706         final String[] values = getResources().getStringArray(R.array.bearer_entries);
707         final StringBuilder retVal = new StringBuilder();
708         boolean first = true;
709         for (String bearer : raw) {
710             int bearerIndex = mBearerMulti.findIndexOfValue(bearer);
711             try {
712                 if (first) {
713                     retVal.append(values[bearerIndex]);
714                     first = false;
715                 } else {
716                     retVal.append(", " + values[bearerIndex]);
717                 }
718             } catch (ArrayIndexOutOfBoundsException e) {
719                 // ignore
720             }
721         }
722         final String val = retVal.toString();
723         if (!TextUtils.isEmpty(val)) {
724             return val;
725         }
726         return null;
727     }
728 
mvnoDescription(String newValue)729     private String mvnoDescription(String newValue) {
730         final int mvnoIndex = mMvnoType.findIndexOfValue(newValue);
731         final String oldValue = mMvnoType.getValue();
732 
733         if (mvnoIndex == -1) {
734             return null;
735         } else {
736             final String[] values = getResources().getStringArray(R.array.mvno_type_entries);
737             final boolean mvnoMatchDataUneditable =
738                     mReadOnlyApn || (mReadOnlyApnFields != null
739                             && Arrays.asList(mReadOnlyApnFields)
740                             .contains(Telephony.Carriers.MVNO_MATCH_DATA));
741             mMvnoMatchData.setEnabled(!mvnoMatchDataUneditable && mvnoIndex != 0);
742             if (newValue != null && !newValue.equals(oldValue)) {
743                 if (values[mvnoIndex].equals("SPN")) {
744                     TelephonyManager telephonyManager = (TelephonyManager)
745                             getContext().getSystemService(TelephonyManager.class);
746                     final TelephonyManager telephonyManagerForSubId =
747                             telephonyManager.createForSubscriptionId(mSubId);
748                     if (telephonyManagerForSubId != null) {
749                         telephonyManager = telephonyManagerForSubId;
750                     }
751                     mMvnoMatchData.setText(telephonyManager.getSimOperatorName());
752                 } else if (values[mvnoIndex].equals("IMSI")) {
753                     final SubscriptionInfo subInfo =
754                             mProxySubscriptionMgr.getAccessibleSubscriptionInfo(mSubId);
755                     final String mcc = (subInfo == null) ? "" :
756                             Objects.toString(subInfo.getMccString(), "");
757                     final String mnc = (subInfo == null) ? "" :
758                             Objects.toString(subInfo.getMncString(), "");
759                     mMvnoMatchData.setText(mcc + mnc + "x");
760                 } else if (values[mvnoIndex].equals("GID")) {
761                     TelephonyManager telephonyManager = (TelephonyManager)
762                             getContext().getSystemService(TelephonyManager.class);
763                     final TelephonyManager telephonyManagerForSubId =
764                             telephonyManager.createForSubscriptionId(mSubId);
765                     if (telephonyManagerForSubId != null) {
766                         telephonyManager = telephonyManagerForSubId;
767                     }
768                     mMvnoMatchData.setText(telephonyManager.getGroupIdLevel1());
769                 } else {
770                     // mvno type 'none' case. At this time, mvnoIndex should be 0.
771                     mMvnoMatchData.setText("");
772                 }
773             }
774 
775             try {
776                 return values[mvnoIndex];
777             } catch (ArrayIndexOutOfBoundsException e) {
778                 return null;
779             }
780         }
781     }
782     /**
783      * Callback when preference status changed.
784      */
onPreferenceChange(Preference preference, Object newValue)785     public boolean onPreferenceChange(Preference preference, Object newValue) {
786         String key = preference.getKey();
787         if (KEY_AUTH_TYPE.equals(key)) {
788             try {
789                 final int index = Integer.parseInt((String) newValue);
790                 mAuthType.setValueIndex(index);
791 
792                 final String[] values = getResources().getStringArray(R.array.apn_auth_entries);
793                 mAuthType.setSummary(values[index]);
794             } catch (NumberFormatException e) {
795                 return false;
796             }
797         } else if (KEY_APN_TYPE.equals(key)) {
798             String data = (TextUtils.isEmpty((String) newValue)
799                     && !ArrayUtils.isEmpty(mDefaultApnTypes))
800                     ? getEditableApnType(mDefaultApnTypes) : (String) newValue;
801             if (!TextUtils.isEmpty(data)) {
802                 mApnType.setSummary(data);
803             }
804         } else if (KEY_PROTOCOL.equals(key)) {
805             final String protocol = protocolDescription((String) newValue, mProtocol);
806             if (protocol == null) {
807                 return false;
808             }
809             mProtocol.setSummary(protocol);
810             mProtocol.setValue((String) newValue);
811         } else if (KEY_ROAMING_PROTOCOL.equals(key)) {
812             final String protocol = protocolDescription((String) newValue, mRoamingProtocol);
813             if (protocol == null) {
814                 return false;
815             }
816             mRoamingProtocol.setSummary(protocol);
817             mRoamingProtocol.setValue((String) newValue);
818         } else if (KEY_BEARER_MULTI.equals(key)) {
819             final String bearer = bearerMultiDescription((Set<String>) newValue);
820             if (bearer == null) {
821                 return false;
822             }
823             mBearerMulti.setValues((Set<String>) newValue);
824             mBearerMulti.setSummary(bearer);
825         } else if (KEY_MVNO_TYPE.equals(key)) {
826             final String mvno = mvnoDescription((String) newValue);
827             if (mvno == null) {
828                 return false;
829             }
830             mMvnoType.setValue((String) newValue);
831             mMvnoType.setSummary(mvno);
832             mMvnoMatchData.setSummary(checkNullforMvnoValue(mMvnoMatchData.getText()));
833         } else if (KEY_PASSWORD.equals(key)) {
834             mPassword.setSummary(starify(newValue != null ? String.valueOf(newValue) : ""));
835         } else if (KEY_CARRIER_ENABLED.equals(key)) {
836             // do nothing
837         } else {
838             preference.setSummary(checkNull(newValue != null ? String.valueOf(newValue) : null));
839         }
840         return true;
841     }
842 
843     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)844     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
845         super.onCreateOptionsMenu(menu, inflater);
846         // If it's a new APN, then cancel will delete the new entry in onPause
847         // If APN add is not allowed, delete might lead to issue regarding recovery
848         if (!mNewApn && !mReadOnlyApn && mIsAddApnAllowed) {
849             menu.add(0, MENU_DELETE, 0, R.string.menu_delete)
850                 .setIcon(R.drawable.ic_delete);
851         }
852         if (!mReadOnlyApn) {
853             menu.add(0, MENU_SAVE, 0, R.string.menu_save)
854                 .setIcon(android.R.drawable.ic_menu_save);
855         }
856         menu.add(0, MENU_CANCEL, 0, R.string.menu_cancel)
857             .setIcon(android.R.drawable.ic_menu_close_clear_cancel);
858     }
859 
860     @Override
onOptionsItemSelected(MenuItem item)861     public boolean onOptionsItemSelected(MenuItem item) {
862         switch (item.getItemId()) {
863             case MENU_DELETE:
864                 deleteApn();
865                 finish();
866                 return true;
867             case MENU_SAVE:
868                 if (validateAndSaveApnData()) {
869                     finish();
870                 }
871                 return true;
872             case MENU_CANCEL:
873                 finish();
874                 return true;
875             default:
876                 return super.onOptionsItemSelected(item);
877         }
878     }
879 
880     @Override
onViewCreated(View view, Bundle savedInstanceState)881     public void onViewCreated(View view, Bundle savedInstanceState) {
882         super.onViewCreated(view, savedInstanceState);
883         view.setOnKeyListener(this);
884         view.setFocusableInTouchMode(true);
885         view.requestFocus();
886     }
887 
888     /**
889      * Try to save the apn data when pressed the back button. An error message will be displayed if
890      * the apn data is invalid.
891      *
892      * TODO(b/77339593): Try to keep the same behavior between back button and up navigate button.
893      * We will save the valid apn data to the database when pressed the back button, but discard all
894      * user changed when pressed the up navigate button.
895      */
896     @Override
onKey(View v, int keyCode, KeyEvent event)897     public boolean onKey(View v, int keyCode, KeyEvent event) {
898         if (event.getAction() != KeyEvent.ACTION_DOWN) return false;
899         switch (keyCode) {
900             case KeyEvent.KEYCODE_BACK: {
901                 if (validateAndSaveApnData()) {
902                     finish();
903                 }
904                 return true;
905             }
906         }
907         return false;
908     }
909 
910     /**
911      * Add key, value to {@code cv} and compare the value against the value at index in
912      * {@link #mApnData}.
913      *
914      * <p>
915      * The key, value will not add to {@code cv} if value is null.
916      *
917      * @return true if values are different. {@code assumeDiff} indicates if values can be assumed
918      * different in which case no comparison is needed.
919      */
setStringValueAndCheckIfDiff( ContentValues cv, String key, String value, boolean assumeDiff, int index)920     boolean setStringValueAndCheckIfDiff(
921             ContentValues cv, String key, String value, boolean assumeDiff, int index) {
922         final String valueFromLocalCache = mApnData.getString(index);
923         if (VDBG) {
924             Log.d(TAG, "setStringValueAndCheckIfDiff: assumeDiff: " + assumeDiff
925                     + " key: " + key
926                     + " value: '" + value
927                     + "' valueFromDb: '" + valueFromLocalCache + "'");
928         }
929         final boolean isDiff = assumeDiff
930                 || !((TextUtils.isEmpty(value) && TextUtils.isEmpty(valueFromLocalCache))
931                 || (value != null && value.equals(valueFromLocalCache)));
932 
933         if (isDiff && value != null) {
934             cv.put(key, value);
935         }
936         return isDiff;
937     }
938 
939     /**
940      * Add key, value to {@code cv} and compare the value against the value at index in
941      * {@link #mApnData}.
942      *
943      * @return true if values are different. {@code assumeDiff} indicates if values can be assumed
944      * different in which case no comparison is needed.
945      */
setIntValueAndCheckIfDiff( ContentValues cv, String key, int value, boolean assumeDiff, int index)946     boolean setIntValueAndCheckIfDiff(
947             ContentValues cv, String key, int value, boolean assumeDiff, int index) {
948         final Integer valueFromLocalCache = mApnData.getInteger(index);
949         if (VDBG) {
950             Log.d(TAG, "setIntValueAndCheckIfDiff: assumeDiff: " + assumeDiff
951                     + " key: " + key
952                     + " value: '" + value
953                     + "' valueFromDb: '" + valueFromLocalCache + "'");
954         }
955 
956         final boolean isDiff = assumeDiff || value != valueFromLocalCache;
957         if (isDiff) {
958             cv.put(key, value);
959         }
960         return isDiff;
961     }
962 
963     /**
964      * Validates the apn data and save it to the database if it's valid.
965      *
966      * <p>
967      * A dialog with error message will be displayed if the APN data is invalid.
968      *
969      * @return true if there is no error
970      */
971     @VisibleForTesting
validateAndSaveApnData()972     boolean validateAndSaveApnData() {
973         // Nothing to do if it's a read only APN
974         if (mReadOnlyApn) {
975             return true;
976         }
977 
978         final String name = checkNotSet(mName.getText());
979         final String apn = checkNotSet(mApn.getText());
980         final String mcc = checkNotSet(mMcc.getText());
981         final String mnc = checkNotSet(mMnc.getText());
982 
983         final String errorMsg = validateApnData();
984         if (errorMsg != null) {
985             showError();
986             return false;
987         }
988 
989         final ContentValues values = new ContentValues();
990         // call update() if it's a new APN. If not, check if any field differs from the db value;
991         // if any diff is found update() should be called
992         boolean callUpdate = mNewApn;
993         callUpdate = setStringValueAndCheckIfDiff(values,
994                 Telephony.Carriers.NAME,
995                 name,
996                 callUpdate,
997                 NAME_INDEX);
998 
999         callUpdate = setStringValueAndCheckIfDiff(values,
1000                 Telephony.Carriers.APN,
1001                 apn,
1002                 callUpdate,
1003                 APN_INDEX);
1004 
1005         callUpdate = setStringValueAndCheckIfDiff(values,
1006                 Telephony.Carriers.PROXY,
1007                 checkNotSet(mProxy.getText()),
1008                 callUpdate,
1009                 PROXY_INDEX);
1010 
1011         callUpdate = setStringValueAndCheckIfDiff(values,
1012                 Telephony.Carriers.PORT,
1013                 checkNotSet(mPort.getText()),
1014                 callUpdate,
1015                 PORT_INDEX);
1016 
1017         callUpdate = setStringValueAndCheckIfDiff(values,
1018                 Telephony.Carriers.MMSPROXY,
1019                 checkNotSet(mMmsProxy.getText()),
1020                 callUpdate,
1021                 MMSPROXY_INDEX);
1022 
1023         callUpdate = setStringValueAndCheckIfDiff(values,
1024                 Telephony.Carriers.MMSPORT,
1025                 checkNotSet(mMmsPort.getText()),
1026                 callUpdate,
1027                 MMSPORT_INDEX);
1028 
1029         callUpdate = setStringValueAndCheckIfDiff(values,
1030                 Telephony.Carriers.USER,
1031                 checkNotSet(mUser.getText()),
1032                 callUpdate,
1033                 USER_INDEX);
1034 
1035         callUpdate = setStringValueAndCheckIfDiff(values,
1036                 Telephony.Carriers.SERVER,
1037                 checkNotSet(mServer.getText()),
1038                 callUpdate,
1039                 SERVER_INDEX);
1040 
1041         callUpdate = setStringValueAndCheckIfDiff(values,
1042                 Telephony.Carriers.PASSWORD,
1043                 checkNotSet(mPassword.getText()),
1044                 callUpdate,
1045                 PASSWORD_INDEX);
1046 
1047         callUpdate = setStringValueAndCheckIfDiff(values,
1048                 Telephony.Carriers.MMSC,
1049                 checkNotSet(mMmsc.getText()),
1050                 callUpdate,
1051                 MMSC_INDEX);
1052 
1053         final String authVal = mAuthType.getValue();
1054         if (authVal != null) {
1055             callUpdate = setIntValueAndCheckIfDiff(values,
1056                     Telephony.Carriers.AUTH_TYPE,
1057                     Integer.parseInt(authVal),
1058                     callUpdate,
1059                     AUTH_TYPE_INDEX);
1060         }
1061 
1062         callUpdate = setStringValueAndCheckIfDiff(values,
1063                 Telephony.Carriers.PROTOCOL,
1064                 checkNotSet(mProtocol.getValue()),
1065                 callUpdate,
1066                 PROTOCOL_INDEX);
1067 
1068         callUpdate = setStringValueAndCheckIfDiff(values,
1069                 Telephony.Carriers.ROAMING_PROTOCOL,
1070                 checkNotSet(mRoamingProtocol.getValue()),
1071                 callUpdate,
1072                 ROAMING_PROTOCOL_INDEX);
1073 
1074         callUpdate = setStringValueAndCheckIfDiff(values,
1075                 Telephony.Carriers.TYPE,
1076                 checkNotSet(getUserEnteredApnType()),
1077                 callUpdate,
1078                 TYPE_INDEX);
1079 
1080         callUpdate = setStringValueAndCheckIfDiff(values,
1081                 Telephony.Carriers.MCC,
1082                 mcc,
1083                 callUpdate,
1084                 MCC_INDEX);
1085 
1086         callUpdate = setStringValueAndCheckIfDiff(values,
1087                 Telephony.Carriers.MNC,
1088                 mnc,
1089                 callUpdate,
1090                 MNC_INDEX);
1091 
1092         values.put(Telephony.Carriers.NUMERIC, mcc + mnc);
1093 
1094         if (mCurMnc != null && mCurMcc != null) {
1095             if (mCurMnc.equals(mnc) && mCurMcc.equals(mcc)) {
1096                 values.put(Telephony.Carriers.CURRENT, 1);
1097             }
1098         }
1099 
1100         final Set<String> bearerSet = mBearerMulti.getValues();
1101         int bearerBitmask = 0;
1102         for (String bearer : bearerSet) {
1103             if (Integer.parseInt(bearer) == 0) {
1104                 bearerBitmask = 0;
1105                 break;
1106             } else {
1107                 bearerBitmask |= getBitmaskForTech(Integer.parseInt(bearer));
1108             }
1109         }
1110         callUpdate = setIntValueAndCheckIfDiff(values,
1111                 Telephony.Carriers.BEARER_BITMASK,
1112                 bearerBitmask,
1113                 callUpdate,
1114                 BEARER_BITMASK_INDEX);
1115 
1116         int bearerVal;
1117         if (bearerBitmask == 0 || mBearerInitialVal == 0) {
1118             bearerVal = 0;
1119         } else if (bitmaskHasTech(bearerBitmask, mBearerInitialVal)) {
1120             bearerVal = mBearerInitialVal;
1121         } else {
1122             // bearer field was being used but bitmask has changed now and does not include the
1123             // initial bearer value -- setting bearer to 0 but maybe better behavior is to choose a
1124             // random tech from the new bitmask??
1125             bearerVal = 0;
1126         }
1127         callUpdate = setIntValueAndCheckIfDiff(values,
1128                 Telephony.Carriers.BEARER,
1129                 bearerVal,
1130                 callUpdate,
1131                 BEARER_INDEX);
1132 
1133         callUpdate = setStringValueAndCheckIfDiff(values,
1134                 Telephony.Carriers.MVNO_TYPE,
1135                 checkNotSet(mMvnoType.getValue()),
1136                 callUpdate,
1137                 MVNO_TYPE_INDEX);
1138 
1139         callUpdate = setStringValueAndCheckIfDiff(values,
1140                 Telephony.Carriers.MVNO_MATCH_DATA,
1141                 checkNotSet(mMvnoMatchData.getText()),
1142                 callUpdate,
1143                 MVNO_MATCH_DATA_INDEX);
1144 
1145         callUpdate = setIntValueAndCheckIfDiff(values,
1146                 Telephony.Carriers.CARRIER_ENABLED,
1147                 mCarrierEnabled.isChecked() ? 1 : 0,
1148                 callUpdate,
1149                 CARRIER_ENABLED_INDEX);
1150 
1151         values.put(Telephony.Carriers.EDITED_STATUS, Telephony.Carriers.USER_EDITED);
1152 
1153         if (callUpdate) {
1154             final Uri uri = mApnData.getUri() == null ? mCarrierUri : mApnData.getUri();
1155             updateApnDataToDatabase(uri, values);
1156         } else {
1157             if (VDBG) Log.d(TAG, "validateAndSaveApnData: not calling update()");
1158         }
1159 
1160         return true;
1161     }
1162 
updateApnDataToDatabase(Uri uri, ContentValues values)1163     private void updateApnDataToDatabase(Uri uri, ContentValues values) {
1164         ThreadUtils.postOnBackgroundThread(() -> {
1165             if (uri.equals(mCarrierUri)) {
1166                 // Add a new apn to the database
1167                 final Uri newUri = getContentResolver().insert(mCarrierUri, values);
1168                 if (newUri == null) {
1169                     Log.e(TAG, "Can't add a new apn to database " + mCarrierUri);
1170                 }
1171             } else {
1172                 // Update the existing apn
1173                 getContentResolver().update(
1174                         uri, values, null /* where */, null /* selection Args */);
1175             }
1176         });
1177     }
1178 
1179     /**
1180      * Validates whether the apn data is valid.
1181      *
1182      * @return An error message if the apn data is invalid, otherwise return null.
1183      */
1184     @VisibleForTesting
validateApnData()1185     String validateApnData() {
1186         String errorMsg = null;
1187 
1188         final String name = checkNotSet(mName.getText());
1189         final String apn = checkNotSet(mApn.getText());
1190         final String mcc = checkNotSet(mMcc.getText());
1191         final String mnc = checkNotSet(mMnc.getText());
1192         boolean doNotCheckMccMnc = mIsCarrierIdApn && TextUtils.isEmpty(mcc)
1193                 && TextUtils.isEmpty(mnc);
1194         if (TextUtils.isEmpty(name)) {
1195             errorMsg = getResources().getString(R.string.error_name_empty);
1196         } else if (TextUtils.isEmpty(apn)) {
1197             errorMsg = getResources().getString(R.string.error_apn_empty);
1198         } else if (doNotCheckMccMnc) {
1199             Log.d(TAG, "validateApnData: carrier id APN does not have mcc/mnc defined");
1200             // no op, skip mcc mnc null check
1201         } else if (mcc == null || mcc.length() != 3) {
1202             errorMsg = getResources().getString(R.string.error_mcc_not3);
1203         } else if ((mnc == null || (mnc.length() & 0xFFFE) != 2)) {
1204             errorMsg = getResources().getString(R.string.error_mnc_not23);
1205         }
1206 
1207         if (errorMsg == null) {
1208             // if carrier does not allow editing certain apn types, make sure type does not include
1209             // those
1210             if (!ArrayUtils.isEmpty(mReadOnlyApnTypes)
1211                     && apnTypesMatch(mReadOnlyApnTypes, getUserEnteredApnType())) {
1212                 final StringBuilder stringBuilder = new StringBuilder();
1213                 for (String type : mReadOnlyApnTypes) {
1214                     stringBuilder.append(type).append(", ");
1215                     Log.d(TAG, "validateApnData: appending type: " + type);
1216                 }
1217                 // remove last ", "
1218                 if (stringBuilder.length() >= 2) {
1219                     stringBuilder.delete(stringBuilder.length() - 2, stringBuilder.length());
1220                 }
1221                 errorMsg = String.format(getResources().getString(R.string.error_adding_apn_type),
1222                         stringBuilder);
1223             }
1224         }
1225 
1226         return errorMsg;
1227     }
1228 
1229     @VisibleForTesting
showError()1230     void showError() {
1231         ErrorDialog.showError(this);
1232     }
1233 
deleteApn()1234     private void deleteApn() {
1235         if (mApnData.getUri() != null) {
1236             getContentResolver().delete(mApnData.getUri(), null, null);
1237             mApnData = new ApnData(sProjection.length);
1238         }
1239     }
1240 
starify(String value)1241     private String starify(String value) {
1242         if (value == null || value.length() == 0) {
1243             return sNotSet;
1244         } else {
1245             final char[] password = new char[value.length()];
1246             for (int i = 0; i < password.length; i++) {
1247                 password[i] = '*';
1248             }
1249             return new String(password);
1250         }
1251     }
1252 
1253     /**
1254      * Returns {@link #sNotSet} if the given string {@code value} is null or empty. The string
1255      * {@link #sNotSet} typically used as the default display when an entry in the preference is
1256      * null or empty.
1257      */
checkNull(String value)1258     private String checkNull(String value) {
1259         return TextUtils.isEmpty(value) ? sNotSet : value;
1260     }
1261 
1262     /**
1263      * To make traslation be diversity, use another string id for MVNO value.
1264      */
checkNullforMvnoValue(String value)1265     private String checkNullforMvnoValue(String value) {
1266         String notSetForMvnoValue = getResources().getString(R.string.apn_not_set_for_mvno);
1267         return TextUtils.isEmpty(value) ? notSetForMvnoValue : value;
1268     }
1269 
1270     /**
1271      * Returns null if the given string {@code value} equals to {@link #sNotSet}. This method
1272      * should be used when convert a string value from preference to database.
1273      */
checkNotSet(String value)1274     private String checkNotSet(String value) {
1275         return sNotSet.equals(value) ? null : value;
1276     }
1277 
1278     @VisibleForTesting
getUserEnteredApnType()1279     String getUserEnteredApnType() {
1280         // if user has not specified a type, map it to "ALL APN TYPES THAT ARE NOT READ-ONLY"
1281         // but if user enter empty type, map it just for default
1282         String userEnteredApnType = mApnType.getText();
1283         if (userEnteredApnType != null) userEnteredApnType = userEnteredApnType.trim();
1284         if ((TextUtils.isEmpty(userEnteredApnType)
1285                 || APN_TYPE_ALL.equals(userEnteredApnType))) {
1286             userEnteredApnType = getEditableApnType(APN_TYPES);
1287         }
1288         Log.d(TAG, "getUserEnteredApnType: changed apn type to editable apn types: "
1289                 + userEnteredApnType);
1290         return userEnteredApnType;
1291     }
1292 
getEditableApnType(String[] apnTypeList)1293     private String getEditableApnType(String[] apnTypeList) {
1294         final StringBuilder editableApnTypes = new StringBuilder();
1295         final List<String> readOnlyApnTypes = Arrays.asList(mReadOnlyApnTypes);
1296         boolean first = true;
1297         for (String apnType : apnTypeList) {
1298             // add APN type if it is not read-only and is not wild-cardable
1299             if (!readOnlyApnTypes.contains(apnType)
1300                     && !apnType.equals(APN_TYPE_IA)
1301                     && !apnType.equals(APN_TYPE_EMERGENCY)
1302                     && !apnType.equals(APN_TYPE_MCX)
1303                     && !apnType.equals(APN_TYPE_IMS)) {
1304                 if (first) {
1305                     first = false;
1306                 } else {
1307                     editableApnTypes.append(",");
1308                 }
1309                 editableApnTypes.append(apnType);
1310             }
1311         }
1312         return editableApnTypes.toString();
1313     }
1314 
initApnEditorUi()1315     private void initApnEditorUi() {
1316         addPreferencesFromResource(R.xml.apn_editor);
1317 
1318         sNotSet = getResources().getString(R.string.apn_not_set);
1319         mName = (EditTextPreference) findPreference("apn_name");
1320         mApn = (EditTextPreference) findPreference("apn_apn");
1321         mProxy = (EditTextPreference) findPreference("apn_http_proxy");
1322         mPort = (EditTextPreference) findPreference("apn_http_port");
1323         mUser = (EditTextPreference) findPreference("apn_user");
1324         mServer = (EditTextPreference) findPreference("apn_server");
1325         mPassword = (EditTextPreference) findPreference(KEY_PASSWORD);
1326         mMmsProxy = (EditTextPreference) findPreference("apn_mms_proxy");
1327         mMmsPort = (EditTextPreference) findPreference("apn_mms_port");
1328         mMmsc = (EditTextPreference) findPreference("apn_mmsc");
1329         mMcc = (EditTextPreference) findPreference("apn_mcc");
1330         mMnc = (EditTextPreference) findPreference("apn_mnc");
1331         mApnType = (EditTextPreference) findPreference("apn_type");
1332         mAuthType = (ListPreference) findPreference(KEY_AUTH_TYPE);
1333         mProtocol = (ListPreference) findPreference(KEY_PROTOCOL);
1334         mRoamingProtocol = (ListPreference) findPreference(KEY_ROAMING_PROTOCOL);
1335         mCarrierEnabled = (SwitchPreference) findPreference(KEY_CARRIER_ENABLED);
1336         mBearerMulti = (MultiSelectListPreference) findPreference(KEY_BEARER_MULTI);
1337         mMvnoType = (ListPreference) findPreference(KEY_MVNO_TYPE);
1338         mMvnoMatchData = (EditTextPreference) findPreference("mvno_match_data");
1339     }
1340 
1341     @VisibleForTesting
getCarrierCustomizedConfig(Context context)1342     protected void getCarrierCustomizedConfig(Context context) {
1343         mReadOnlyApn = false;
1344         mReadOnlyApnTypes = null;
1345         mReadOnlyApnFields = null;
1346         mIsAddApnAllowed = true;
1347 
1348         final CarrierConfigManager configManager = (CarrierConfigManager)
1349             context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
1350         if (configManager != null) {
1351             final PersistableBundle b = configManager.getConfigForSubId(mSubId);
1352             if (b != null) {
1353                 mReadOnlyApnTypes = b.getStringArray(
1354                         CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY);
1355                 if (!ArrayUtils.isEmpty(mReadOnlyApnTypes)) {
1356                     Log.d(TAG,
1357                             "onCreate: read only APN type: " + Arrays.toString(mReadOnlyApnTypes));
1358                 }
1359                 mReadOnlyApnFields = b.getStringArray(
1360                         CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY);
1361 
1362                 mDefaultApnTypes = b.getStringArray(
1363                         CarrierConfigManager.KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY);
1364 
1365                 if (!ArrayUtils.isEmpty(mDefaultApnTypes)) {
1366                     Log.d(TAG, "onCreate: default apn types: " + Arrays.toString(mDefaultApnTypes));
1367                 }
1368 
1369                 mDefaultApnProtocol = b.getString(
1370                         CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_PROTOCOL_STRING);
1371                 if (!TextUtils.isEmpty(mDefaultApnProtocol)) {
1372                     Log.d(TAG, "onCreate: default apn protocol: " + mDefaultApnProtocol);
1373                 }
1374 
1375                 mDefaultApnRoamingProtocol = b.getString(
1376                         CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING);
1377                 if (!TextUtils.isEmpty(mDefaultApnRoamingProtocol)) {
1378                     Log.d(TAG, "onCreate: default apn roaming protocol: "
1379                             + mDefaultApnRoamingProtocol);
1380                 }
1381 
1382                 mIsAddApnAllowed = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL);
1383                 if (!mIsAddApnAllowed) {
1384                     Log.d(TAG, "onCreate: not allow to add new APN");
1385                 }
1386             }
1387         }
1388     }
1389 
setCarrierCustomizedConfigToUi()1390     private void setCarrierCustomizedConfigToUi() {
1391         if (TextUtils.isEmpty(mApnType.getText()) && !ArrayUtils.isEmpty(mDefaultApnTypes)) {
1392             String value = getEditableApnType(mDefaultApnTypes);
1393             mApnType.setText(value);
1394             mApnType.setSummary(value);
1395         }
1396 
1397         String protocol = protocolDescription(mDefaultApnProtocol, mProtocol);
1398         if (TextUtils.isEmpty(mProtocol.getValue()) && !TextUtils.isEmpty(protocol)) {
1399             mProtocol.setValue(mDefaultApnProtocol);
1400             mProtocol.setSummary(protocol);
1401         }
1402 
1403         String roamingProtocol = protocolDescription(mDefaultApnRoamingProtocol, mRoamingProtocol);
1404         if (TextUtils.isEmpty(mRoamingProtocol.getValue()) && !TextUtils.isEmpty(roamingProtocol)) {
1405             mRoamingProtocol.setValue(mDefaultApnRoamingProtocol);
1406             mRoamingProtocol.setSummary(roamingProtocol);
1407         }
1408     }
1409 
1410     /**
1411      * Dialog of error message.
1412      */
1413     public static class ErrorDialog extends InstrumentedDialogFragment {
1414         /**
1415          * Show error dialog.
1416          */
showError(ApnEditor editor)1417         public static void showError(ApnEditor editor) {
1418             final ErrorDialog dialog = new ErrorDialog();
1419             dialog.setTargetFragment(editor, 0);
1420             dialog.show(editor.getFragmentManager(), "error");
1421         }
1422 
1423         @Override
onCreateDialog(Bundle savedInstanceState)1424         public Dialog onCreateDialog(Bundle savedInstanceState) {
1425             final String msg = ((ApnEditor) getTargetFragment()).validateApnData();
1426 
1427             return new AlertDialog.Builder(getContext())
1428                     .setTitle(R.string.error_title)
1429                     .setPositiveButton(android.R.string.ok, null)
1430                     .setMessage(msg)
1431                     .create();
1432         }
1433 
1434         @Override
getMetricsCategory()1435         public int getMetricsCategory() {
1436             return SettingsEnums.DIALOG_APN_EDITOR_ERROR;
1437         }
1438     }
1439 
1440     @VisibleForTesting
getApnDataFromUri(Uri uri)1441     ApnData getApnDataFromUri(Uri uri) {
1442         ApnData apnData = null;
1443         try (Cursor cursor = getContentResolver().query(
1444                 uri,
1445                 sProjection,
1446                 null /* selection */,
1447                 null /* selectionArgs */,
1448                 null /* sortOrder */)) {
1449             if (cursor != null) {
1450                 cursor.moveToFirst();
1451                 apnData = new ApnData(uri, cursor);
1452             }
1453         }
1454 
1455         if (apnData == null) {
1456             Log.d(TAG, "Can't get apnData from Uri " + uri);
1457         }
1458 
1459         return apnData;
1460     }
1461 
1462     @VisibleForTesting
isUserRestricted()1463     boolean isUserRestricted() {
1464         UserManager userManager = getContext().getSystemService(UserManager.class);
1465         if (userManager == null) {
1466             return false;
1467         }
1468         if (!userManager.isAdminUser()) {
1469             Log.e(TAG, "User is not an admin");
1470             return true;
1471         }
1472         if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) {
1473             Log.e(TAG, "User is not allowed to configure mobile network");
1474             return true;
1475         }
1476         return false;
1477     }
1478 
1479     @VisibleForTesting
1480     static class ApnData {
1481         /**
1482          * The uri correspond to a database row of the apn data. This should be null if the apn
1483          * is not in the database.
1484          */
1485         Uri mUri;
1486 
1487         /** Each element correspond to a column of the database row. */
1488         Object[] mData;
1489 
ApnData(int numberOfField)1490         ApnData(int numberOfField) {
1491             mData = new Object[numberOfField];
1492         }
1493 
ApnData(Uri uri, Cursor cursor)1494         ApnData(Uri uri, Cursor cursor) {
1495             mUri = uri;
1496             mData = new Object[cursor.getColumnCount()];
1497             for (int i = 0; i < mData.length; i++) {
1498                 switch (cursor.getType(i)) {
1499                     case Cursor.FIELD_TYPE_FLOAT:
1500                         mData[i] = cursor.getFloat(i);
1501                         break;
1502                     case Cursor.FIELD_TYPE_INTEGER:
1503                         mData[i] = cursor.getInt(i);
1504                         break;
1505                     case Cursor.FIELD_TYPE_STRING:
1506                         mData[i] = cursor.getString(i);
1507                         break;
1508                     case Cursor.FIELD_TYPE_BLOB:
1509                         mData[i] = cursor.getBlob(i);
1510                         break;
1511                     default:
1512                         mData[i] = null;
1513                 }
1514             }
1515         }
1516 
getUri()1517         Uri getUri() {
1518             return mUri;
1519         }
1520 
setUri(Uri uri)1521         void setUri(Uri uri) {
1522             mUri = uri;
1523         }
1524 
getInteger(int index)1525         Integer getInteger(int index) {
1526             return (Integer) mData[index];
1527         }
1528 
getInteger(int index, Integer defaultValue)1529         Integer getInteger(int index, Integer defaultValue) {
1530             final Integer val = getInteger(index);
1531             return val == null ? defaultValue : val;
1532         }
1533 
getString(int index)1534         String getString(int index) {
1535             return (String) mData[index];
1536         }
1537     }
1538 
getBitmaskForTech(int radioTech)1539     private static int getBitmaskForTech(int radioTech) {
1540         if (radioTech >= 1) {
1541             return (1 << (radioTech - 1));
1542         }
1543         return 0;
1544     }
1545 
bitmaskHasTech(int bearerBitmask, int radioTech)1546     private static boolean bitmaskHasTech(int bearerBitmask, int radioTech) {
1547         if (bearerBitmask == 0) {
1548             return true;
1549         } else if (radioTech >= 1) {
1550             return ((bearerBitmask & (1 << (radioTech - 1))) != 0);
1551         }
1552         return false;
1553     }
1554 }
1555