2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings;
19 import android.app.AlertDialog;
20 import android.app.Dialog;
21 import android.content.ContentUris;
22 import android.content.ContentValues;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.res.Resources;
26 import android.database.Cursor;
27 import android.net.Uri;
28 import android.os.Bundle;
29 import android.os.PersistableBundle;
30 import android.provider.Telephony;
31 import android.support.v14.preference.MultiSelectListPreference;
32 import android.support.v14.preference.SwitchPreference;
33 import android.support.v7.preference.EditTextPreference;
34 import android.support.v7.preference.ListPreference;
35 import android.support.v7.preference.Preference;
36 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
37 import android.telephony.CarrierConfigManager;
38 import android.telephony.ServiceState;
39 import android.telephony.SubscriptionManager;
40 import android.telephony.TelephonyManager;
41 import android.text.TextUtils;
42 import android.util.Log;
43 import android.view.KeyEvent;
44 import android.view.Menu;
45 import android.view.MenuInflater;
46 import android.view.MenuItem;
47 import android.view.View;
48 import android.view.View.OnKeyListener;
50 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
51 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
52 import com.android.internal.telephony.PhoneConstants;
53 import com.android.internal.util.ArrayUtils;
55 import java.util.ArrayList;
56 import java.util.Arrays;
57 import java.util.HashSet;
58 import java.util.List;
61 import static android.app.Activity.RESULT_OK;
62 import static android.content.Context.TELEPHONY_SERVICE;
64 public class ApnEditor extends SettingsPreferenceFragment
65 implements OnPreferenceChangeListener, OnKeyListener {
67 private final static String TAG = ApnEditor.class.getSimpleName();
68 private final static boolean VDBG = false; // STOPSHIP if true
70 private final static String SAVED_POS = "pos";
71 private final static String KEY_AUTH_TYPE = "auth_type";
72 private final static String KEY_PROTOCOL = "apn_protocol";
73 private final static String KEY_ROAMING_PROTOCOL = "apn_roaming_protocol";
74 private final static String KEY_CARRIER_ENABLED = "carrier_enabled";
75 private final static String KEY_BEARER_MULTI = "bearer_multi";
76 private final static String KEY_MVNO_TYPE = "mvno_type";
77 private final static String KEY_PASSWORD = "apn_password";
79 private static final int MENU_DELETE = Menu.FIRST;
80 private static final int MENU_SAVE = Menu.FIRST + 1;
81 private static final int MENU_CANCEL = Menu.FIRST + 2;
83 private static String sNotSet;
84 private EditTextPreference mName;
85 private EditTextPreference mApn;
86 private EditTextPreference mProxy;
87 private EditTextPreference mPort;
88 private EditTextPreference mUser;
89 private EditTextPreference mServer;
90 private EditTextPreference mPassword;
91 private EditTextPreference mMmsc;
92 private EditTextPreference mMcc;
93 private EditTextPreference mMnc;
94 private EditTextPreference mMmsProxy;
95 private EditTextPreference mMmsPort;
96 private ListPreference mAuthType;
97 private EditTextPreference mApnType;
98 private ListPreference mProtocol;
99 private ListPreference mRoamingProtocol;
100 private SwitchPreference mCarrierEnabled;
101 private MultiSelectListPreference mBearerMulti;
102 private ListPreference mMvnoType;
103 private EditTextPreference mMvnoMatchData;
105 private String mCurMnc;
106 private String mCurMcc;
109 private Cursor mCursor;
110 private boolean mNewApn;
111 private boolean mFirstTime;
113 private Resources mRes;
114 private TelephonyManager mTelephonyManager;
115 private int mBearerInitialVal = 0;
116 private String mMvnoTypeStr;
117 private String mMvnoMatchDataStr;
118 private String[] mReadOnlyApnTypes;
119 private String[] mReadOnlyApnFields;
120 private boolean mReadOnlyApn;
123 * Standard projection for the interesting columns of a normal note.
125 private static final String[] sProjection = new String[] {
126 Telephony.Carriers._ID, // 0
127 Telephony.Carriers.NAME, // 1
128 Telephony.Carriers.APN, // 2
129 Telephony.Carriers.PROXY, // 3
130 Telephony.Carriers.PORT, // 4
131 Telephony.Carriers.USER, // 5
132 Telephony.Carriers.SERVER, // 6
133 Telephony.Carriers.PASSWORD, // 7
134 Telephony.Carriers.MMSC, // 8
135 Telephony.Carriers.MCC, // 9
136 Telephony.Carriers.MNC, // 10
137 Telephony.Carriers.NUMERIC, // 11
138 Telephony.Carriers.MMSPROXY,// 12
139 Telephony.Carriers.MMSPORT, // 13
140 Telephony.Carriers.AUTH_TYPE, // 14
141 Telephony.Carriers.TYPE, // 15
142 Telephony.Carriers.PROTOCOL, // 16
143 Telephony.Carriers.CARRIER_ENABLED, // 17
144 Telephony.Carriers.BEARER, // 18
145 Telephony.Carriers.BEARER_BITMASK, // 19
146 Telephony.Carriers.ROAMING_PROTOCOL, // 20
147 Telephony.Carriers.MVNO_TYPE, // 21
148 Telephony.Carriers.MVNO_MATCH_DATA, // 22
149 Telephony.Carriers.EDITED, // 23
150 Telephony.Carriers.USER_EDITABLE //24
153 private static final int ID_INDEX = 0;
154 private static final int NAME_INDEX = 1;
155 private static final int APN_INDEX = 2;
156 private static final int PROXY_INDEX = 3;
157 private static final int PORT_INDEX = 4;
158 private static final int USER_INDEX = 5;
159 private static final int SERVER_INDEX = 6;
160 private static final int PASSWORD_INDEX = 7;
161 private static final int MMSC_INDEX = 8;
162 private static final int MCC_INDEX = 9;
163 private static final int MNC_INDEX = 10;
164 private static final int MMSPROXY_INDEX = 12;
165 private static final int MMSPORT_INDEX = 13;
166 private static final int AUTH_TYPE_INDEX = 14;
167 private static final int TYPE_INDEX = 15;
168 private static final int PROTOCOL_INDEX = 16;
169 private static final int CARRIER_ENABLED_INDEX = 17;
170 private static final int BEARER_INDEX = 18;
171 private static final int BEARER_BITMASK_INDEX = 19;
172 private static final int ROAMING_PROTOCOL_INDEX = 20;
173 private static final int MVNO_TYPE_INDEX = 21;
174 private static final int MVNO_MATCH_DATA_INDEX = 22;
175 private static final int EDITED_INDEX = 23;
176 private static final int USER_EDITABLE_INDEX = 24;
180 public void onCreate(Bundle icicle) {
181 super.onCreate(icicle);
183 addPreferencesFromResource(R.xml.apn_editor);
185 sNotSet = getResources().getString(R.string.apn_not_set);
186 mName = (EditTextPreference) findPreference("apn_name");
187 mApn = (EditTextPreference) findPreference("apn_apn");
188 mProxy = (EditTextPreference) findPreference("apn_http_proxy");
189 mPort = (EditTextPreference) findPreference("apn_http_port");
190 mUser = (EditTextPreference) findPreference("apn_user");
191 mServer = (EditTextPreference) findPreference("apn_server");
192 mPassword = (EditTextPreference) findPreference(KEY_PASSWORD);
193 mMmsProxy = (EditTextPreference) findPreference("apn_mms_proxy");
194 mMmsPort = (EditTextPreference) findPreference("apn_mms_port");
195 mMmsc = (EditTextPreference) findPreference("apn_mmsc");
196 mMcc = (EditTextPreference) findPreference("apn_mcc");
197 mMnc = (EditTextPreference) findPreference("apn_mnc");
198 mApnType = (EditTextPreference) findPreference("apn_type");
199 mAuthType = (ListPreference) findPreference(KEY_AUTH_TYPE);
200 mProtocol = (ListPreference) findPreference(KEY_PROTOCOL);
201 mRoamingProtocol = (ListPreference) findPreference(KEY_ROAMING_PROTOCOL);
202 mCarrierEnabled = (SwitchPreference) findPreference(KEY_CARRIER_ENABLED);
203 mBearerMulti = (MultiSelectListPreference) findPreference(KEY_BEARER_MULTI);
204 mMvnoType = (ListPreference) findPreference(KEY_MVNO_TYPE);
205 mMvnoMatchData = (EditTextPreference) findPreference("mvno_match_data");
207 mRes = getResources();
209 final Intent intent = getIntent();
210 final String action = intent.getAction();
211 mSubId = intent.getIntExtra(ApnSettings.SUB_ID,
212 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
214 mFirstTime = icicle == null;
215 mReadOnlyApn = false;
216 mReadOnlyApnTypes = null;
217 mReadOnlyApnFields = null;
219 CarrierConfigManager configManager = (CarrierConfigManager)
220 getSystemService(Context.CARRIER_CONFIG_SERVICE);
221 if (configManager != null) {
222 PersistableBundle b = configManager.getConfig();
224 mReadOnlyApnTypes = b.getStringArray(
225 CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY);
226 if (!ArrayUtils.isEmpty(mReadOnlyApnTypes)) {
227 for (String apnType : mReadOnlyApnTypes) {
228 Log.d(TAG, "onCreate: read only APN type: " + apnType);
231 mReadOnlyApnFields = b.getStringArray(
232 CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY);
236 if (action.equals(Intent.ACTION_EDIT)) {
237 Uri uri = intent.getData();
238 if (!uri.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) {
239 Log.e(TAG, "Edit request not for carrier table. Uri: " + uri);
244 } else if (action.equals(Intent.ACTION_INSERT)) {
245 if (mFirstTime || icicle.getInt(SAVED_POS) == 0) {
246 Uri uri = intent.getData();
247 if (!uri.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) {
248 Log.e(TAG, "Insert request not for carrier table. Uri: " + uri);
252 mUri = getContentResolver().insert(uri, new ContentValues());
254 mUri = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI,
255 icicle.getInt(SAVED_POS));
258 mMvnoTypeStr = intent.getStringExtra(ApnSettings.MVNO_TYPE);
259 mMvnoMatchDataStr = intent.getStringExtra(ApnSettings.MVNO_MATCH_DATA);
260 // If we were unable to create a new note, then just finish
261 // this activity. A RESULT_CANCELED will be sent back to the
262 // original activity if they requested a result.
264 Log.w(TAG, "Failed to insert new telephony provider into "
265 + getIntent().getData());
270 // The new entry was created, so assume all will end well and
271 // set the result to be returned.
272 setResult(RESULT_OK, (new Intent()).setAction(mUri.toString()));
279 mCursor = getActivity().managedQuery(mUri, sProjection, null, null);
280 mCursor.moveToFirst();
282 mTelephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
284 Log.d(TAG, "onCreate: EDITED " + mCursor.getInt(EDITED_INDEX));
285 // if it's not a USER_EDITED apn, check if it's read-only
286 if (mCursor.getInt(EDITED_INDEX) != Telephony.Carriers.USER_EDITED &&
287 (mCursor.getInt(USER_EDITABLE_INDEX) == 0 ||
288 apnTypesMatch(mReadOnlyApnTypes, mCursor.getString(TYPE_INDEX)))) {
289 Log.d(TAG, "onCreate: apnTypesMatch; read-only APN");
292 } else if (!ArrayUtils.isEmpty(mReadOnlyApnFields)) {
293 disableFields(mReadOnlyApnFields);
296 for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) {
297 getPreferenceScreen().getPreference(i).setOnPreferenceChangeListener(this);
303 public void onActivityCreated(Bundle savedInstanceState) {
304 super.onActivityCreated(savedInstanceState);
309 * Check if passed in array of APN types indicates all APN types
310 * @param apnTypes array of APN types. "*" indicates all types.
311 * @return true if all apn types are included in the array, false otherwise
313 static boolean hasAllApns(String[] apnTypes) {
314 if (ArrayUtils.isEmpty(apnTypes)) {
318 List apnList = Arrays.asList(apnTypes);
319 if (apnList.contains(PhoneConstants.APN_TYPE_ALL)) {
320 Log.d(TAG, "hasAllApns: true because apnList.contains(PhoneConstants.APN_TYPE_ALL)");
323 for (String apn : PhoneConstants.APN_TYPES) {
324 if (!apnList.contains(apn)) {
329 Log.d(TAG, "hasAllApns: true");
334 * Check if APN types overlap.
335 * @param apnTypesArray1 array of APNs. Empty array indicates no APN type; "*" indicates all
337 * @param apnTypes2 comma separated string of APN types. Empty string represents all types.
338 * @return if any apn type matches return true, otherwise return false
340 private boolean apnTypesMatch(String[] apnTypesArray1, String apnTypes2) {
341 if (ArrayUtils.isEmpty(apnTypesArray1)) {
345 if (hasAllApns(apnTypesArray1) || TextUtils.isEmpty(apnTypes2)) {
349 List apnTypesList1 = Arrays.asList(apnTypesArray1);
350 String[] apnTypesArray2 = apnTypes2.split(",");
352 for (String apn : apnTypesArray2) {
353 if (apnTypesList1.contains(apn.trim())) {
354 Log.d(TAG, "apnTypesMatch: true because match found for " + apn.trim());
359 Log.d(TAG, "apnTypesMatch: false");
364 * Function to get Preference obj corresponding to an apnField
365 * @param apnField apn field name for which pref is needed
366 * @return Preference obj corresponding to passed in apnField
368 private Preference getPreferenceFromFieldName(String apnField) {
370 case Telephony.Carriers.NAME:
372 case Telephony.Carriers.APN:
374 case Telephony.Carriers.PROXY:
376 case Telephony.Carriers.PORT:
378 case Telephony.Carriers.USER:
380 case Telephony.Carriers.SERVER:
382 case Telephony.Carriers.PASSWORD:
384 case Telephony.Carriers.MMSPROXY:
386 case Telephony.Carriers.MMSPORT:
388 case Telephony.Carriers.MMSC:
390 case Telephony.Carriers.MCC:
392 case Telephony.Carriers.MNC:
394 case Telephony.Carriers.TYPE:
396 case Telephony.Carriers.AUTH_TYPE:
398 case Telephony.Carriers.PROTOCOL:
400 case Telephony.Carriers.ROAMING_PROTOCOL:
401 return mRoamingProtocol;
402 case Telephony.Carriers.CARRIER_ENABLED:
403 return mCarrierEnabled;
404 case Telephony.Carriers.BEARER:
405 case Telephony.Carriers.BEARER_BITMASK:
407 case Telephony.Carriers.MVNO_TYPE:
409 case Telephony.Carriers.MVNO_MATCH_DATA:
410 return mMvnoMatchData;
416 * Disables given fields so that user cannot modify them
418 * @param apnFields fields to be disabled
420 private void disableFields(String[] apnFields) {
421 for (String apnField : apnFields) {
422 Preference preference = getPreferenceFromFieldName(apnField);
423 if (preference != null) {
424 preference.setEnabled(false);
430 * Disables all fields so that user cannot modify the APN
432 private void disableAllFields() {
433 mName.setEnabled(false);
434 mApn.setEnabled(false);
435 mProxy.setEnabled(false);
436 mPort.setEnabled(false);
437 mUser.setEnabled(false);
438 mServer.setEnabled(false);
439 mPassword.setEnabled(false);
440 mMmsProxy.setEnabled(false);
441 mMmsPort.setEnabled(false);
442 mMmsc.setEnabled(false);
443 mMcc.setEnabled(false);
444 mMnc.setEnabled(false);
445 mApnType.setEnabled(false);
446 mAuthType.setEnabled(false);
447 mProtocol.setEnabled(false);
448 mRoamingProtocol.setEnabled(false);
449 mCarrierEnabled.setEnabled(false);
450 mBearerMulti.setEnabled(false);
451 mMvnoType.setEnabled(false);
452 mMvnoMatchData.setEnabled(false);
456 public int getMetricsCategory() {
457 return MetricsEvent.APN_EDITOR;
461 public void onResume() {
464 if (mUri == null && mNewApn) {
465 // The URI could have been deleted when activity is paused,
466 // therefore, it needs to be restored.
467 mUri = getContentResolver().insert(getIntent().getData(), new ContentValues());
469 Log.w(TAG, "Failed to insert new telephony provider into "
470 + getIntent().getData());
474 mCursor = getActivity().managedQuery(mUri, sProjection, null, null);
475 mCursor.moveToFirst();
481 public void onPause() {
485 private void fillUi() {
488 // Fill in all the values from the db in both text editor and summary
489 mName.setText(mCursor.getString(NAME_INDEX));
490 mApn.setText(mCursor.getString(APN_INDEX));
491 mProxy.setText(mCursor.getString(PROXY_INDEX));
492 mPort.setText(mCursor.getString(PORT_INDEX));
493 mUser.setText(mCursor.getString(USER_INDEX));
494 mServer.setText(mCursor.getString(SERVER_INDEX));
495 mPassword.setText(mCursor.getString(PASSWORD_INDEX));
496 mMmsProxy.setText(mCursor.getString(MMSPROXY_INDEX));
497 mMmsPort.setText(mCursor.getString(MMSPORT_INDEX));
498 mMmsc.setText(mCursor.getString(MMSC_INDEX));
499 mMcc.setText(mCursor.getString(MCC_INDEX));
500 mMnc.setText(mCursor.getString(MNC_INDEX));
501 mApnType.setText(mCursor.getString(TYPE_INDEX));
503 String numeric = mTelephonyManager.getSimOperator(mSubId);
504 // MCC is first 3 chars and then in 2 - 3 chars of MNC
505 if (numeric != null && numeric.length() > 4) {
507 String mcc = numeric.substring(0, 3);
509 String mnc = numeric.substring(3);
510 // Auto populate MNC and MCC for new entries, based on what SIM reports
517 int authVal = mCursor.getInt(AUTH_TYPE_INDEX);
519 mAuthType.setValueIndex(authVal);
521 mAuthType.setValue(null);
524 mProtocol.setValue(mCursor.getString(PROTOCOL_INDEX));
525 mRoamingProtocol.setValue(mCursor.getString(ROAMING_PROTOCOL_INDEX));
526 mCarrierEnabled.setChecked(mCursor.getInt(CARRIER_ENABLED_INDEX)==1);
527 mBearerInitialVal = mCursor.getInt(BEARER_INDEX);
529 HashSet<String> bearers = new HashSet<String>();
530 int bearerBitmask = mCursor.getInt(BEARER_BITMASK_INDEX);
531 if (bearerBitmask == 0) {
532 if (mBearerInitialVal == 0) {
537 while (bearerBitmask != 0) {
538 if ((bearerBitmask & 1) == 1) {
546 if (mBearerInitialVal != 0 && bearers.contains("" + mBearerInitialVal) == false) {
547 // add mBearerInitialVal to bearers
548 bearers.add("" + mBearerInitialVal);
550 mBearerMulti.setValues(bearers);
552 mMvnoType.setValue(mCursor.getString(MVNO_TYPE_INDEX));
553 mMvnoMatchData.setEnabled(false);
554 mMvnoMatchData.setText(mCursor.getString(MVNO_MATCH_DATA_INDEX));
555 if (mNewApn && mMvnoTypeStr != null && mMvnoMatchDataStr != null) {
556 mMvnoType.setValue(mMvnoTypeStr);
557 mMvnoMatchData.setText(mMvnoMatchDataStr);
561 mName.setSummary(checkNull(mName.getText()));
562 mApn.setSummary(checkNull(mApn.getText()));
563 mProxy.setSummary(checkNull(mProxy.getText()));
564 mPort.setSummary(checkNull(mPort.getText()));
565 mUser.setSummary(checkNull(mUser.getText()));
566 mServer.setSummary(checkNull(mServer.getText()));
567 mPassword.setSummary(starify(mPassword.getText()));
568 mMmsProxy.setSummary(checkNull(mMmsProxy.getText()));
569 mMmsPort.setSummary(checkNull(mMmsPort.getText()));
570 mMmsc.setSummary(checkNull(mMmsc.getText()));
571 mMcc.setSummary(checkNull(mMcc.getText()));
572 mMnc.setSummary(checkNull(mMnc.getText()));
573 mApnType.setSummary(checkNull(mApnType.getText()));
575 String authVal = mAuthType.getValue();
576 if (authVal != null) {
577 int authValIndex = Integer.parseInt(authVal);
578 mAuthType.setValueIndex(authValIndex);
580 String []values = mRes.getStringArray(R.array.apn_auth_entries);
581 mAuthType.setSummary(values[authValIndex]);
583 mAuthType.setSummary(sNotSet);
586 mProtocol.setSummary(checkNull(protocolDescription(mProtocol.getValue(), mProtocol)));
587 mRoamingProtocol.setSummary(
588 checkNull(protocolDescription(mRoamingProtocol.getValue(), mRoamingProtocol)));
589 mBearerMulti.setSummary(
590 checkNull(bearerMultiDescription(mBearerMulti.getValues())));
591 mMvnoType.setSummary(
592 checkNull(mvnoDescription(mMvnoType.getValue())));
593 mMvnoMatchData.setSummary(checkNull(mMvnoMatchData.getText()));
594 // allow user to edit carrier_enabled for some APN
595 boolean ceEditable = getResources().getBoolean(R.bool.config_allow_edit_carrier_enabled);
597 mCarrierEnabled.setEnabled(true);
599 mCarrierEnabled.setEnabled(false);
604 * Returns the UI choice (e.g., "IPv4/IPv6") corresponding to the given
605 * raw value of the protocol preference (e.g., "IPV4V6"). If unknown,
608 private String protocolDescription(String raw, ListPreference protocol) {
609 int protocolIndex = protocol.findIndexOfValue(raw);
610 if (protocolIndex == -1) {
613 String[] values = mRes.getStringArray(R.array.apn_protocol_entries);
615 return values[protocolIndex];
616 } catch (ArrayIndexOutOfBoundsException e) {
622 private String bearerDescription(String raw) {
623 int mBearerIndex = mBearerMulti.findIndexOfValue(raw);
624 if (mBearerIndex == -1) {
627 String[] values = mRes.getStringArray(R.array.bearer_entries);
629 return values[mBearerIndex];
630 } catch (ArrayIndexOutOfBoundsException e) {
636 private String bearerMultiDescription(Set<String> raw) {
637 String[] values = mRes.getStringArray(R.array.bearer_entries);
638 StringBuilder retVal = new StringBuilder();
639 boolean first = true;
640 for (String bearer : raw) {
641 int bearerIndex = mBearerMulti.findIndexOfValue(bearer);
644 retVal.append(values[bearerIndex]);
647 retVal.append(", " + values[bearerIndex]);
649 } catch (ArrayIndexOutOfBoundsException e) {
653 String val = retVal.toString();
654 if (!TextUtils.isEmpty(val)) {
660 private String mvnoDescription(String newValue) {
661 int mvnoIndex = mMvnoType.findIndexOfValue(newValue);
662 String oldValue = mMvnoType.getValue();
664 if (mvnoIndex == -1) {
667 String[] values = mRes.getStringArray(R.array.mvno_type_entries);
668 boolean mvnoMatchDataUneditable =
669 mReadOnlyApn || (mReadOnlyApnFields != null
670 && Arrays.asList(mReadOnlyApnFields)
671 .contains(Telephony.Carriers.MVNO_MATCH_DATA));
672 mMvnoMatchData.setEnabled(!mvnoMatchDataUneditable && mvnoIndex != 0);
673 if (newValue != null && newValue.equals(oldValue) == false) {
674 if (values[mvnoIndex].equals("SPN")) {
675 mMvnoMatchData.setText(mTelephonyManager.getSimOperatorName());
676 } else if (values[mvnoIndex].equals("IMSI")) {
677 String numeric = mTelephonyManager.getSimOperator(mSubId);
678 mMvnoMatchData.setText(numeric + "x");
679 } else if (values[mvnoIndex].equals("GID")) {
680 mMvnoMatchData.setText(mTelephonyManager.getGroupIdLevel1());
685 return values[mvnoIndex];
686 } catch (ArrayIndexOutOfBoundsException e) {
692 public boolean onPreferenceChange(Preference preference, Object newValue) {
693 String key = preference.getKey();
694 if (KEY_AUTH_TYPE.equals(key)) {
696 int index = Integer.parseInt((String) newValue);
697 mAuthType.setValueIndex(index);
699 String[] values = mRes.getStringArray(R.array.apn_auth_entries);
700 mAuthType.setSummary(values[index]);
701 } catch (NumberFormatException e) {
704 } else if (KEY_PROTOCOL.equals(key)) {
705 String protocol = protocolDescription((String) newValue, mProtocol);
706 if (protocol == null) {
709 mProtocol.setSummary(protocol);
710 mProtocol.setValue((String) newValue);
711 } else if (KEY_ROAMING_PROTOCOL.equals(key)) {
712 String protocol = protocolDescription((String) newValue, mRoamingProtocol);
713 if (protocol == null) {
716 mRoamingProtocol.setSummary(protocol);
717 mRoamingProtocol.setValue((String) newValue);
718 } else if (KEY_BEARER_MULTI.equals(key)) {
719 String bearer = bearerMultiDescription((Set<String>) newValue);
720 if (bearer == null) {
723 mBearerMulti.setValues((Set<String>) newValue);
724 mBearerMulti.setSummary(bearer);
725 } else if (KEY_MVNO_TYPE.equals(key)) {
726 String mvno = mvnoDescription((String) newValue);
730 mMvnoType.setValue((String) newValue);
731 mMvnoType.setSummary(mvno);
732 } else if (KEY_PASSWORD.equals(key)) {
733 mPassword.setSummary(starify(newValue != null ? String.valueOf(newValue) : ""));
734 } else if (KEY_CARRIER_ENABLED.equals(key)) {
737 preference.setSummary(checkNull(newValue != null ? String.valueOf(newValue) : null));
744 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
745 super.onCreateOptionsMenu(menu, inflater);
746 // If it's a new APN, then cancel will delete the new entry in onPause
747 if (!mNewApn && !mReadOnlyApn) {
748 menu.add(0, MENU_DELETE, 0, R.string.menu_delete)
749 .setIcon(R.drawable.ic_menu_delete);
751 menu.add(0, MENU_SAVE, 0, R.string.menu_save)
752 .setIcon(android.R.drawable.ic_menu_save);
753 menu.add(0, MENU_CANCEL, 0, R.string.menu_cancel)
754 .setIcon(android.R.drawable.ic_menu_close_clear_cancel);
758 public boolean onOptionsItemSelected(MenuItem item) {
759 switch (item.getItemId()) {
764 if (validateAndSave(false)) {
770 getContentResolver().delete(mUri, null, null);
775 return super.onOptionsItemSelected(item);
779 public void onViewCreated(View view, Bundle savedInstanceState) {
780 super.onViewCreated(view, savedInstanceState);
781 view.setOnKeyListener(this);
782 view.setFocusableInTouchMode(true);
786 public boolean onKey(View v, int keyCode, KeyEvent event) {
787 if (event.getAction() != KeyEvent.ACTION_DOWN) return false;
789 case KeyEvent.KEYCODE_BACK: {
790 if (validateAndSave(false)) {
800 public void onSaveInstanceState(Bundle icicle) {
801 super.onSaveInstanceState(icicle);
802 if (validateAndSave(true)) {
803 icicle.putInt(SAVED_POS, mCursor.getInt(ID_INDEX));
808 * Add key, value to cv and compare the value against the value at index in mCursor. Return true
809 * if values are different. assumeDiff indicates if values can be assumed different in which
810 * case no comparison is needed.
811 * @return true if value is different from the value at index in mCursor
813 boolean setStringValueAndCheckIfDiff(ContentValues cv, String key, String value,
814 boolean assumeDiff, int index) {
816 String valueFromCursor = mCursor.getString(index);
818 Log.d(TAG, "setStringValueAndCheckIfDiff: assumeDiff: " + assumeDiff
820 + " value: '" + value
821 + "' valueFromCursor: '" + valueFromCursor + "'");
824 || !((TextUtils.isEmpty(value) && TextUtils.isEmpty(valueFromCursor))
825 || (value != null && value.equals(valueFromCursor)));
829 * Add key, value to cv and compare the value against the value at index in mCursor. Return true
830 * if values are different. assumeDiff indicates if values can be assumed different in which
831 * case no comparison is needed.
832 * @return true if value is different from the value at index in mCursor
834 boolean setIntValueAndCheckIfDiff(ContentValues cv, String key, int value,
835 boolean assumeDiff, int index) {
837 int valueFromCursor = mCursor.getInt(index);
839 Log.d(TAG, "setIntValueAndCheckIfDiff: assumeDiff: " + assumeDiff
841 + " value: '" + value
842 + "' valueFromCursor: '" + valueFromCursor + "'");
844 return assumeDiff || value != valueFromCursor;
848 * Check the key fields' validity and save if valid.
849 * @param force save even if the fields are not valid, if the app is
851 * @return true if there's no error
853 private boolean validateAndSave(boolean force) {
854 // nothing to do if it's a read only APN
859 String name = checkNotSet(mName.getText());
860 String apn = checkNotSet(mApn.getText());
861 String mcc = checkNotSet(mMcc.getText());
862 String mnc = checkNotSet(mMnc.getText());
864 if (getErrorMsg() != null && !force) {
865 ErrorDialog.showError(this);
869 if (!mCursor.moveToFirst()) {
871 "Could not go to the first row in the Cursor when saving data.");
875 // If it's a new APN and a name or apn haven't been entered, then erase the entry
876 if (force && mNewApn && name.length() < 1 && apn.length() < 1) {
877 getContentResolver().delete(mUri, null, null);
882 ContentValues values = new ContentValues();
883 // call update() if it's a new APN. If not, check if any field differs from the db value;
884 // if any diff is found update() should be called
885 boolean callUpdate = mNewApn;
887 // Add a dummy name "Untitled", if the user exits the screen without adding a name but
888 // entered other information worth keeping.
889 callUpdate = setStringValueAndCheckIfDiff(values,
890 Telephony.Carriers.NAME,
891 name.length() < 1 ? getResources().getString(R.string.untitled_apn) : name,
895 callUpdate = setStringValueAndCheckIfDiff(values,
896 Telephony.Carriers.APN,
901 callUpdate = setStringValueAndCheckIfDiff(values,
902 Telephony.Carriers.PROXY,
903 checkNotSet(mProxy.getText()),
907 callUpdate = setStringValueAndCheckIfDiff(values,
908 Telephony.Carriers.PORT,
909 checkNotSet(mPort.getText()),
913 callUpdate = setStringValueAndCheckIfDiff(values,
914 Telephony.Carriers.MMSPROXY,
915 checkNotSet(mMmsProxy.getText()),
919 callUpdate = setStringValueAndCheckIfDiff(values,
920 Telephony.Carriers.MMSPORT,
921 checkNotSet(mMmsPort.getText()),
925 callUpdate = setStringValueAndCheckIfDiff(values,
926 Telephony.Carriers.USER,
927 checkNotSet(mUser.getText()),
931 callUpdate = setStringValueAndCheckIfDiff(values,
932 Telephony.Carriers.SERVER,
933 checkNotSet(mServer.getText()),
937 callUpdate = setStringValueAndCheckIfDiff(values,
938 Telephony.Carriers.PASSWORD,
939 checkNotSet(mPassword.getText()),
943 callUpdate = setStringValueAndCheckIfDiff(values,
944 Telephony.Carriers.MMSC,
945 checkNotSet(mMmsc.getText()),
949 String authVal = mAuthType.getValue();
950 if (authVal != null) {
951 callUpdate = setIntValueAndCheckIfDiff(values,
952 Telephony.Carriers.AUTH_TYPE,
953 Integer.parseInt(authVal),
958 callUpdate = setStringValueAndCheckIfDiff(values,
959 Telephony.Carriers.PROTOCOL,
960 checkNotSet(mProtocol.getValue()),
964 callUpdate = setStringValueAndCheckIfDiff(values,
965 Telephony.Carriers.ROAMING_PROTOCOL,
966 checkNotSet(mRoamingProtocol.getValue()),
968 ROAMING_PROTOCOL_INDEX);
970 callUpdate = setStringValueAndCheckIfDiff(values,
971 Telephony.Carriers.TYPE,
972 checkNotSet(getUserEnteredApnType()),
976 callUpdate = setStringValueAndCheckIfDiff(values,
977 Telephony.Carriers.MCC,
982 callUpdate = setStringValueAndCheckIfDiff(values,
983 Telephony.Carriers.MNC,
988 values.put(Telephony.Carriers.NUMERIC, mcc + mnc);
990 if (mCurMnc != null && mCurMcc != null) {
991 if (mCurMnc.equals(mnc) && mCurMcc.equals(mcc)) {
992 values.put(Telephony.Carriers.CURRENT, 1);
996 Set<String> bearerSet = mBearerMulti.getValues();
997 int bearerBitmask = 0;
998 for (String bearer : bearerSet) {
999 if (Integer.parseInt(bearer) == 0) {
1003 bearerBitmask |= ServiceState.getBitmaskForTech(Integer.parseInt(bearer));
1006 callUpdate = setIntValueAndCheckIfDiff(values,
1007 Telephony.Carriers.BEARER_BITMASK,
1010 BEARER_BITMASK_INDEX);
1013 if (bearerBitmask == 0 || mBearerInitialVal == 0) {
1015 } else if (ServiceState.bitmaskHasTech(bearerBitmask, mBearerInitialVal)) {
1016 bearerVal = mBearerInitialVal;
1018 // bearer field was being used but bitmask has changed now and does not include the
1019 // initial bearer value -- setting bearer to 0 but maybe better behavior is to choose a
1020 // random tech from the new bitmask??
1023 callUpdate = setIntValueAndCheckIfDiff(values,
1024 Telephony.Carriers.BEARER,
1029 callUpdate = setStringValueAndCheckIfDiff(values,
1030 Telephony.Carriers.MVNO_TYPE,
1031 checkNotSet(mMvnoType.getValue()),
1035 callUpdate = setStringValueAndCheckIfDiff(values,
1036 Telephony.Carriers.MVNO_MATCH_DATA,
1037 checkNotSet(mMvnoMatchData.getText()),
1039 MVNO_MATCH_DATA_INDEX);
1041 callUpdate = setIntValueAndCheckIfDiff(values,
1042 Telephony.Carriers.CARRIER_ENABLED,
1043 mCarrierEnabled.isChecked() ? 1 : 0,
1045 CARRIER_ENABLED_INDEX);
1048 getContentResolver().update(mUri, values, null, null);
1050 if (VDBG) Log.d(TAG, "validateAndSave: not calling update()");
1056 private String getErrorMsg() {
1057 String errorMsg = null;
1059 String name = checkNotSet(mName.getText());
1060 String apn = checkNotSet(mApn.getText());
1061 String mcc = checkNotSet(mMcc.getText());
1062 String mnc = checkNotSet(mMnc.getText());
1064 if (name.length() < 1) {
1065 errorMsg = mRes.getString(R.string.error_name_empty);
1066 } else if (apn.length() < 1) {
1067 errorMsg = mRes.getString(R.string.error_apn_empty);
1068 } else if (mcc.length() != 3) {
1069 errorMsg = mRes.getString(R.string.error_mcc_not3);
1070 } else if ((mnc.length() & 0xFFFE) != 2) {
1071 errorMsg = mRes.getString(R.string.error_mnc_not23);
1074 if (errorMsg == null) {
1075 // if carrier does not allow editing certain apn types, make sure type does not include
1077 if (!ArrayUtils.isEmpty(mReadOnlyApnTypes)
1078 && apnTypesMatch(mReadOnlyApnTypes, getUserEnteredApnType())) {
1079 StringBuilder stringBuilder = new StringBuilder();
1080 for (String type : mReadOnlyApnTypes) {
1081 stringBuilder.append(type).append(", ");
1082 Log.d(TAG, "getErrorMsg: appending type: " + type);
1085 if (stringBuilder.length() >= 2) {
1086 stringBuilder.delete(stringBuilder.length() - 2, stringBuilder.length());
1088 errorMsg = String.format(mRes.getString(R.string.error_adding_apn_type),
1096 private void deleteApn() {
1097 getContentResolver().delete(mUri, null, null);
1101 private String starify(String value) {
1102 if (value == null || value.length() == 0) {
1105 char[] password = new char[value.length()];
1106 for (int i = 0; i < password.length; i++) {
1109 return new String(password);
1113 private String checkNull(String value) {
1114 if (value == null || value.length() == 0) {
1121 private String checkNotSet(String value) {
1122 if (value == null || value.equals(sNotSet)) {
1129 private String getUserEnteredApnType() {
1130 // if user has not specified a type, map it to "ALL APN TYPES THAT ARE NOT READ-ONLY"
1131 String userEnteredApnType = mApnType.getText();
1132 if (userEnteredApnType != null) userEnteredApnType = userEnteredApnType.trim();
1133 if ((TextUtils.isEmpty(userEnteredApnType)
1134 || PhoneConstants.APN_TYPE_ALL.equals(userEnteredApnType))
1135 && !ArrayUtils.isEmpty(mReadOnlyApnTypes)) {
1136 StringBuilder editableApnTypes = new StringBuilder();
1137 List<String> readOnlyApnTypes = Arrays.asList(mReadOnlyApnTypes);
1138 boolean first = true;
1139 for (String apnType : PhoneConstants.APN_TYPES) {
1140 // add APN type if it is not read-only and is not wild-cardable
1141 if (!readOnlyApnTypes.contains(apnType)
1142 && !apnType.equals(PhoneConstants.APN_TYPE_IA)
1143 && !apnType.equals(PhoneConstants.APN_TYPE_EMERGENCY)) {
1147 editableApnTypes.append(",");
1149 editableApnTypes.append(apnType);
1152 userEnteredApnType = editableApnTypes.toString();
1153 Log.d(TAG, "getUserEnteredApnType: changed apn type to editable apn types: "
1154 + userEnteredApnType);
1157 return userEnteredApnType;
1160 public static class ErrorDialog extends InstrumentedDialogFragment {
1162 public static void showError(ApnEditor editor) {
1163 ErrorDialog dialog = new ErrorDialog();
1164 dialog.setTargetFragment(editor, 0);
1165 dialog.show(editor.getFragmentManager(), "error");
1169 public Dialog onCreateDialog(Bundle savedInstanceState) {
1170 String msg = ((ApnEditor) getTargetFragment()).getErrorMsg();
1172 return new AlertDialog.Builder(getContext())
1173 .setTitle(R.string.error_title)
1174 .setPositiveButton(android.R.string.ok, null)
1180 public int getMetricsCategory() {
1181 return MetricsEvent.DIALOG_APN_EDITOR_ERROR;