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.Intent;
24 import android.content.SharedPreferences;
25 import android.content.res.Resources;
26 import android.database.Cursor;
27 import android.net.Uri;
28 import android.os.Bundle;
29 import android.preference.EditTextPreference;
30 import android.preference.ListPreference;
31 import android.preference.MultiSelectListPreference;
32 import android.preference.Preference;
33 import android.preference.SwitchPreference;
34 import android.provider.Telephony;
35 import android.telephony.ServiceState;
36 import android.telephony.SubscriptionManager;
37 import android.telephony.TelephonyManager;
38 import android.text.TextUtils;
39 import android.util.Log;
40 import android.view.KeyEvent;
41 import android.view.Menu;
42 import android.view.MenuItem;
43 import com.android.internal.logging.MetricsLogger;
45 import java.util.ArrayList;
46 import java.util.HashSet;
47 import java.util.Iterator;
48 import java.util.List;
51 public class ApnEditor extends InstrumentedPreferenceActivity
52 implements SharedPreferences.OnSharedPreferenceChangeListener,
53 Preference.OnPreferenceChangeListener {
55 private final static String TAG = ApnEditor.class.getSimpleName();
57 private final static String SAVED_POS = "pos";
58 private final static String KEY_AUTH_TYPE = "auth_type";
59 private final static String KEY_PROTOCOL = "apn_protocol";
60 private final static String KEY_ROAMING_PROTOCOL = "apn_roaming_protocol";
61 private final static String KEY_CARRIER_ENABLED = "carrier_enabled";
62 private final static String KEY_BEARER_MULTI = "bearer_multi";
63 private final static String KEY_MVNO_TYPE = "mvno_type";
65 private final static String PROTOCOL_IPV4V6= "IPV4V6";
67 private static final int MENU_DELETE = Menu.FIRST;
68 private static final int MENU_SAVE = Menu.FIRST + 1;
69 private static final int MENU_CANCEL = Menu.FIRST + 2;
70 private static final int ERROR_DIALOG_ID = 0;
71 private static final int DUPLICATE_DIALOG_ID = 1;
73 private static String sNotSet;
74 private EditTextPreference mName;
75 private EditTextPreference mApn;
76 private EditTextPreference mProxy;
77 private EditTextPreference mPort;
78 private EditTextPreference mUser;
79 private EditTextPreference mServer;
80 private EditTextPreference mPassword;
81 private EditTextPreference mMmsc;
82 private EditTextPreference mMcc;
83 private EditTextPreference mMnc;
84 private EditTextPreference mMmsProxy;
85 private EditTextPreference mMmsPort;
86 private ListPreference mAuthType;
87 private EditTextPreference mApnType;
88 private ListPreference mProtocol;
89 private ListPreference mRoamingProtocol;
90 private SwitchPreference mCarrierEnabled;
91 private MultiSelectListPreference mBearerMulti;
92 private ListPreference mMvnoType;
93 private EditTextPreference mMvnoMatchData;
94 private EditTextPreference mPppNumber;
96 private String mCurMnc;
97 private String mCurMcc;
98 private boolean mDisableEditor = false;
101 private Cursor mCursor;
102 private boolean mNewApn;
103 private boolean mFirstTime;
105 private Resources mRes;
106 private TelephonyManager mTelephonyManager;
107 private int mBearerInitialVal = 0;
108 private String mMvnoTypeStr;
109 private String mMvnoMatchDataStr;
112 * Standard projection for the interesting columns of a normal note.
114 private static final String[] sProjection = new String[] {
115 Telephony.Carriers._ID, // 0
116 Telephony.Carriers.NAME, // 1
117 Telephony.Carriers.APN, // 2
118 Telephony.Carriers.PROXY, // 3
119 Telephony.Carriers.PORT, // 4
120 Telephony.Carriers.USER, // 5
121 Telephony.Carriers.SERVER, // 6
122 Telephony.Carriers.PASSWORD, // 7
123 Telephony.Carriers.MMSC, // 8
124 Telephony.Carriers.MCC, // 9
125 Telephony.Carriers.MNC, // 10
126 Telephony.Carriers.NUMERIC, // 11
127 Telephony.Carriers.MMSPROXY,// 12
128 Telephony.Carriers.MMSPORT, // 13
129 Telephony.Carriers.AUTH_TYPE, // 14
130 Telephony.Carriers.TYPE, // 15
131 Telephony.Carriers.PROTOCOL, // 16
132 Telephony.Carriers.CARRIER_ENABLED, // 17
133 Telephony.Carriers.BEARER, // 18
134 Telephony.Carriers.BEARER_BITMASK, // 19
135 Telephony.Carriers.ROAMING_PROTOCOL, // 20
136 Telephony.Carriers.MVNO_TYPE, // 21
137 Telephony.Carriers.MVNO_MATCH_DATA, // 22
141 private static final int ID_INDEX = 0;
142 private static final int NAME_INDEX = 1;
143 private static final int APN_INDEX = 2;
144 private static final int PROXY_INDEX = 3;
145 private static final int PORT_INDEX = 4;
146 private static final int USER_INDEX = 5;
147 private static final int SERVER_INDEX = 6;
148 private static final int PASSWORD_INDEX = 7;
149 private static final int MMSC_INDEX = 8;
150 private static final int MCC_INDEX = 9;
151 private static final int MNC_INDEX = 10;
152 private static final int MMSPROXY_INDEX = 12;
153 private static final int MMSPORT_INDEX = 13;
154 private static final int AUTH_TYPE_INDEX = 14;
155 private static final int TYPE_INDEX = 15;
156 private static final int PROTOCOL_INDEX = 16;
157 private static final int CARRIER_ENABLED_INDEX = 17;
158 private static final int BEARER_INDEX = 18;
159 private static final int BEARER_BITMASK_INDEX = 19;
160 private static final int ROAMING_PROTOCOL_INDEX = 20;
161 private static final int MVNO_TYPE_INDEX = 21;
162 private static final int MVNO_MATCH_DATA_INDEX = 22;
163 private static final int PPP_NUMBER_INDEX = 23;
167 protected void onCreate(Bundle icicle) {
168 super.onCreate(icicle);
170 addPreferencesFromResource(R.xml.apn_editor);
172 sNotSet = getResources().getString(R.string.apn_not_set);
173 mName = (EditTextPreference) findPreference("apn_name");
174 mApn = (EditTextPreference) findPreference("apn_apn");
175 mProxy = (EditTextPreference) findPreference("apn_http_proxy");
176 mPort = (EditTextPreference) findPreference("apn_http_port");
177 mUser = (EditTextPreference) findPreference("apn_user");
178 mServer = (EditTextPreference) findPreference("apn_server");
179 mPassword = (EditTextPreference) findPreference("apn_password");
180 mMmsProxy = (EditTextPreference) findPreference("apn_mms_proxy");
181 mMmsPort = (EditTextPreference) findPreference("apn_mms_port");
182 mMmsc = (EditTextPreference) findPreference("apn_mmsc");
183 mMcc = (EditTextPreference) findPreference("apn_mcc");
184 mMnc = (EditTextPreference) findPreference("apn_mnc");
185 mApnType = (EditTextPreference) findPreference("apn_type");
186 mPppNumber = (EditTextPreference) findPreference("apn_ppp_number");
188 mAuthType = (ListPreference) findPreference(KEY_AUTH_TYPE);
189 mAuthType.setOnPreferenceChangeListener(this);
191 mProtocol = (ListPreference) findPreference(KEY_PROTOCOL);
192 mProtocol.setOnPreferenceChangeListener(this);
194 mRoamingProtocol = (ListPreference) findPreference(KEY_ROAMING_PROTOCOL);
195 mRoamingProtocol.setOnPreferenceChangeListener(this);
197 mCarrierEnabled = (SwitchPreference) findPreference(KEY_CARRIER_ENABLED);
199 mBearerMulti = (MultiSelectListPreference) findPreference(KEY_BEARER_MULTI);
200 mBearerMulti.setOnPreferenceChangeListener(this);
202 mMvnoType = (ListPreference) findPreference(KEY_MVNO_TYPE);
203 mMvnoType.setOnPreferenceChangeListener(this);
204 mMvnoMatchData = (EditTextPreference) findPreference("mvno_match_data");
206 mRes = getResources();
208 final Intent intent = getIntent();
209 final String action = intent.getAction();
210 mSubId = intent.getIntExtra(ApnSettings.SUB_ID,
211 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
212 mDisableEditor = intent.getBooleanExtra("DISABLE_EDITOR", false);
213 if (mDisableEditor) {
214 getPreferenceScreen().setEnabled(false);
215 Log.d(TAG, "ApnEditor form is disabled.");
218 mFirstTime = icicle == null;
220 if (action.equals(Intent.ACTION_EDIT)) {
221 mUri = intent.getData();
222 } else if (action.equals(Intent.ACTION_INSERT)) {
223 if (mFirstTime || icicle.getInt(SAVED_POS) == 0) {
224 ContentValues values = new ContentValues();
225 values.put(Telephony.Carriers.PROTOCOL, PROTOCOL_IPV4V6);
226 values.put(Telephony.Carriers.ROAMING_PROTOCOL, PROTOCOL_IPV4V6);
227 mUri = getContentResolver().insert(intent.getData(), values);
229 mUri = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI,
230 icicle.getInt(SAVED_POS));
233 mMvnoTypeStr = intent.getStringExtra(ApnSettings.MVNO_TYPE);
234 mMvnoMatchDataStr = intent.getStringExtra(ApnSettings.MVNO_MATCH_DATA);
235 // If we were unable to create a new note, then just finish
236 // this activity. A RESULT_CANCELED will be sent back to the
237 // original activity if they requested a result.
239 Log.w(TAG, "Failed to insert new telephony provider into "
240 + getIntent().getData());
245 // The new entry was created, so assume all will end well and
246 // set the result to be returned.
247 setResult(RESULT_OK, (new Intent()).setAction(mUri.toString()));
254 mCursor = managedQuery(mUri, sProjection, null, null);
255 mCursor.moveToFirst();
257 mTelephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
263 protected int getMetricsCategory() {
264 return MetricsLogger.APN_EDITOR;
268 public void onResume() {
270 getPreferenceScreen().getSharedPreferences()
271 .registerOnSharedPreferenceChangeListener(this);
275 public void onPause() {
276 getPreferenceScreen().getSharedPreferences()
277 .unregisterOnSharedPreferenceChangeListener(this);
281 private void fillUi() {
284 String numeric = mTelephonyManager.getIccOperatorNumericForData(mSubId);
285 // Fill in all the values from the db in both text editor and summary
286 mName.setText(mCursor.getString(NAME_INDEX));
287 mApn.setText(mCursor.getString(APN_INDEX));
288 mProxy.setText(mCursor.getString(PROXY_INDEX));
289 mPort.setText(mCursor.getString(PORT_INDEX));
290 mUser.setText(mCursor.getString(USER_INDEX));
291 mServer.setText(mCursor.getString(SERVER_INDEX));
292 mPassword.setText(mCursor.getString(PASSWORD_INDEX));
293 mMmsProxy.setText(mCursor.getString(MMSPROXY_INDEX));
294 mMmsPort.setText(mCursor.getString(MMSPORT_INDEX));
295 mMmsc.setText(mCursor.getString(MMSC_INDEX));
296 mMcc.setText(mCursor.getString(MCC_INDEX));
297 mMnc.setText(mCursor.getString(MNC_INDEX));
298 mApnType.setText(mCursor.getString(TYPE_INDEX));
300 // MCC is first 3 chars and then in 2 - 3 chars of MNC
301 if (numeric != null && numeric.length() > 4) {
303 String mcc = numeric.substring(0, 3);
305 String mnc = numeric.substring(3);
306 // Auto populate MNC and MCC for new entries, based on what SIM reports
312 mApnType.setText(checkNull(getString(R.string.config_default_new_apn_type)));
314 int authVal = mCursor.getInt(AUTH_TYPE_INDEX);
316 mAuthType.setValueIndex(authVal);
318 mAuthType.setValue(null);
321 mProtocol.setValue(mCursor.getString(PROTOCOL_INDEX));
322 mRoamingProtocol.setValue(mCursor.getString(ROAMING_PROTOCOL_INDEX));
323 mCarrierEnabled.setChecked(mCursor.getInt(CARRIER_ENABLED_INDEX)==1);
324 mBearerInitialVal = mCursor.getInt(BEARER_INDEX);
326 HashSet<String> bearers = new HashSet<String>();
327 int bearerBitmask = mCursor.getInt(BEARER_BITMASK_INDEX);
328 if (bearerBitmask == 0) {
329 if (mBearerInitialVal == 0) {
334 while (bearerBitmask != 0) {
335 if ((bearerBitmask & 1) == 1) {
343 if (mBearerInitialVal != 0 && bearers.contains("" + mBearerInitialVal) == false) {
344 // add mBearerInitialVal to bearers
345 bearers.add("" + mBearerInitialVal);
347 mBearerMulti.setValues(bearers);
349 mMvnoType.setValue(mCursor.getString(MVNO_TYPE_INDEX));
350 mMvnoMatchData.setEnabled(false);
351 mMvnoMatchData.setText(mCursor.getString(MVNO_MATCH_DATA_INDEX));
352 if (mNewApn && mMvnoTypeStr != null && mMvnoMatchDataStr != null) {
353 mMvnoType.setValue(mMvnoTypeStr);
354 mMvnoMatchData.setText(mMvnoMatchDataStr);
357 String pppNumber = mCursor.getString(PPP_NUMBER_INDEX);
358 mPppNumber.setText(pppNumber);
359 if (pppNumber == null) {
361 getPreferenceScreen().removePreference(mPppNumber);
362 } else if (getResources().getBoolean(R.bool.config_ppp_enabled)) {
363 getPreferenceScreen().removePreference(mPppNumber);
368 mName.setSummary(checkNull(mName.getText()));
369 mApn.setSummary(checkNull(mApn.getText()));
370 mProxy.setSummary(checkNull(mProxy.getText()));
371 mPort.setSummary(checkNull(mPort.getText()));
372 mUser.setSummary(checkNull(mUser.getText()));
373 mServer.setSummary(checkNull(mServer.getText()));
374 mPassword.setSummary(starify(mPassword.getText()));
375 mMmsProxy.setSummary(checkNull(mMmsProxy.getText()));
376 mMmsPort.setSummary(checkNull(mMmsPort.getText()));
377 mMmsc.setSummary(checkNull(mMmsc.getText()));
378 mMcc.setSummary(checkNull(mMcc.getText()));
379 mMnc.setSummary(checkNull(mMnc.getText()));
380 mApnType.setSummary(checkNull(mApnType.getText()));
382 String pppNumber = mPppNumber.getText();
383 if (pppNumber != null) {
384 // Remove this preference if PPP number is not present
385 // in the APN settings
386 mPppNumber.setSummary(checkNull(pppNumber));
389 String authVal = mAuthType.getValue();
390 if (authVal != null) {
391 int authValIndex = Integer.parseInt(authVal);
392 mAuthType.setValueIndex(authValIndex);
394 String []values = mRes.getStringArray(R.array.apn_auth_entries);
395 mAuthType.setSummary(values[authValIndex]);
397 mAuthType.setSummary(sNotSet);
400 mProtocol.setSummary(
401 checkNull(protocolDescription(mProtocol.getValue(), mProtocol)));
402 mRoamingProtocol.setSummary(
403 checkNull(protocolDescription(mRoamingProtocol.getValue(), mRoamingProtocol)));
404 mBearerMulti.setSummary(
405 checkNull(bearerMultiDescription(mBearerMulti.getValues())));
406 mMvnoType.setSummary(
407 checkNull(mvnoDescription(mMvnoType.getValue())));
408 mMvnoMatchData.setSummary(checkNull(mMvnoMatchData.getText()));
409 // allow user to edit carrier_enabled for some APN
410 boolean ceEditable = getResources().getBoolean(R.bool.config_allow_edit_carrier_enabled);
412 mCarrierEnabled.setEnabled(true);
414 mCarrierEnabled.setEnabled(false);
419 * Returns the UI choice (e.g., "IPv4/IPv6") corresponding to the given
420 * raw value of the protocol preference (e.g., "IPV4V6"). If unknown,
423 private String protocolDescription(String raw, ListPreference protocol) {
424 int protocolIndex = protocol.findIndexOfValue(raw);
425 if (protocolIndex == -1) {
428 String[] values = mRes.getStringArray(R.array.apn_protocol_entries);
430 return values[protocolIndex];
431 } catch (ArrayIndexOutOfBoundsException e) {
437 private String bearerDescription(String raw) {
438 int mBearerIndex = mBearerMulti.findIndexOfValue(raw);
439 if (mBearerIndex == -1) {
442 String[] values = mRes.getStringArray(R.array.bearer_entries);
444 return values[mBearerIndex];
445 } catch (ArrayIndexOutOfBoundsException e) {
451 private String bearerMultiDescription(Set<String> raw) {
452 String[] values = mRes.getStringArray(R.array.bearer_entries);
453 StringBuilder retVal = new StringBuilder();
454 boolean first = true;
455 for (String bearer : raw) {
456 int bearerIndex = mBearerMulti.findIndexOfValue(bearer);
459 retVal.append(values[bearerIndex]);
462 retVal.append(", " + values[bearerIndex]);
464 } catch (ArrayIndexOutOfBoundsException e) {
468 String val = retVal.toString();
469 if (!TextUtils.isEmpty(val)) {
475 private String mvnoDescription(String newValue) {
476 int mvnoIndex = mMvnoType.findIndexOfValue(newValue);
477 String oldValue = mMvnoType.getValue();
479 if (mvnoIndex == -1) {
482 String[] values = mRes.getStringArray(R.array.mvno_type_entries);
483 if (values[mvnoIndex].equals("None")) {
484 mMvnoMatchData.setEnabled(false);
486 mMvnoMatchData.setEnabled(true);
488 if (newValue != null && newValue.equals(oldValue) == false) {
489 if (values[mvnoIndex].equals("SPN")) {
490 mMvnoMatchData.setText(mTelephonyManager.getSimOperatorName());
491 } else if (values[mvnoIndex].equals("IMSI")) {
492 String numeric = mTelephonyManager.getIccOperatorNumericForData(mSubId);
493 mMvnoMatchData.setText(numeric + "x");
494 } else if (values[mvnoIndex].equals("GID")) {
495 mMvnoMatchData.setText(mTelephonyManager.getGroupIdLevel1());
500 return values[mvnoIndex];
501 } catch (ArrayIndexOutOfBoundsException e) {
507 public boolean onPreferenceChange(Preference preference, Object newValue) {
508 String key = preference.getKey();
509 if (KEY_AUTH_TYPE.equals(key)) {
511 int index = Integer.parseInt((String) newValue);
512 mAuthType.setValueIndex(index);
514 String []values = mRes.getStringArray(R.array.apn_auth_entries);
515 mAuthType.setSummary(values[index]);
516 } catch (NumberFormatException e) {
519 } else if (KEY_PROTOCOL.equals(key)) {
520 String protocol = protocolDescription((String) newValue, mProtocol);
521 if (protocol == null) {
524 mProtocol.setSummary(protocol);
525 mProtocol.setValue((String) newValue);
526 } else if (KEY_ROAMING_PROTOCOL.equals(key)) {
527 String protocol = protocolDescription((String) newValue, mRoamingProtocol);
528 if (protocol == null) {
531 mRoamingProtocol.setSummary(protocol);
532 mRoamingProtocol.setValue((String) newValue);
533 } else if (KEY_BEARER_MULTI.equals(key)) {
534 String bearer = bearerMultiDescription((Set<String>) newValue);
535 if (bearer == null) {
538 mBearerMulti.setValues((Set<String>) newValue);
539 mBearerMulti.setSummary(bearer);
540 } else if (KEY_MVNO_TYPE.equals(key)) {
541 String mvno = mvnoDescription((String) newValue);
545 mMvnoType.setValue((String) newValue);
546 mMvnoType.setSummary(mvno);
553 public boolean onCreateOptionsMenu(Menu menu) {
554 super.onCreateOptionsMenu(menu);
555 if (mDisableEditor) {
556 Log.d(TAG, "Form is disabled. Do not create the options menu.");
559 // If it's a new APN, then cancel will delete the new entry in onPause
561 menu.add(0, MENU_DELETE, 0, R.string.menu_delete)
562 .setIcon(R.drawable.ic_menu_delete);
564 menu.add(0, MENU_SAVE, 0, R.string.menu_save)
565 .setIcon(android.R.drawable.ic_menu_save);
566 menu.add(0, MENU_CANCEL, 0, R.string.menu_cancel)
567 .setIcon(android.R.drawable.ic_menu_close_clear_cancel);
572 public boolean onOptionsItemSelected(MenuItem item) {
573 switch (item.getItemId()) {
578 if (validateAndSave(false)) {
584 getContentResolver().delete(mUri, null, null);
589 return super.onOptionsItemSelected(item);
593 public boolean onKeyDown(int keyCode, KeyEvent event) {
595 case KeyEvent.KEYCODE_BACK: {
596 if (validateAndSave(false)) {
602 return super.onKeyDown(keyCode, event);
606 protected void onSaveInstanceState(Bundle icicle) {
607 super.onSaveInstanceState(icicle);
608 if (validateAndSave(true)) {
609 icicle.putInt(SAVED_POS, mCursor.getInt(ID_INDEX));
614 * Check the key fields' validity and save if valid.
615 * @param force save even if the fields are not valid, if the app is
617 * @return true if the data was saved
619 private boolean validateAndSave(boolean force) {
621 // If the form is not editable, do nothing and return.
623 Log.d(TAG, "Form is disabled. Nothing to save.");
627 String name = checkNotSet(mName.getText());
628 String apn = checkNotSet(mApn.getText());
629 String mcc = checkNotSet(mMcc.getText());
630 String mnc = checkNotSet(mMnc.getText());
632 if (getErrorMsg() != null && !force) {
633 showDialog(ERROR_DIALOG_ID);
637 if (!mCursor.moveToFirst()) {
639 "Could not go to the first row in the Cursor when saving data.");
643 // If it's a new APN and a name or apn haven't been entered, then erase the entry
644 if (force && mNewApn && name.length() < 1 && apn.length() < 1) {
645 getContentResolver().delete(mUri, null, null);
649 ContentValues values = new ContentValues();
651 // Add a dummy name "Untitled", if the user exits the screen without adding a name but
652 // entered other information worth keeping.
653 values.put(Telephony.Carriers.NAME,
654 name.length() < 1 ? getResources().getString(R.string.untitled_apn) : name);
655 values.put(Telephony.Carriers.APN, apn);
656 values.put(Telephony.Carriers.PROXY, checkNotSet(mProxy.getText()));
657 values.put(Telephony.Carriers.PORT, checkNotSet(mPort.getText()));
658 values.put(Telephony.Carriers.MMSPROXY, checkNotSet(mMmsProxy.getText()));
659 values.put(Telephony.Carriers.MMSPORT, checkNotSet(mMmsPort.getText()));
660 values.put(Telephony.Carriers.USER, checkNotSet(mUser.getText()));
661 values.put(Telephony.Carriers.SERVER, checkNotSet(mServer.getText()));
662 values.put(Telephony.Carriers.PASSWORD, checkNotSet(mPassword.getText()));
663 values.put(Telephony.Carriers.MMSC, checkNotSet(mMmsc.getText()));
665 String authVal = mAuthType.getValue();
666 if (authVal != null) {
667 values.put(Telephony.Carriers.AUTH_TYPE, Integer.parseInt(authVal));
670 values.put(Telephony.Carriers.PROTOCOL, checkNotSet(mProtocol.getValue()));
671 values.put(Telephony.Carriers.ROAMING_PROTOCOL, checkNotSet(mRoamingProtocol.getValue()));
673 values.put(Telephony.Carriers.TYPE, checkNotSet(mApnType.getText()));
675 values.put(Telephony.Carriers.MCC, mcc);
676 values.put(Telephony.Carriers.MNC, mnc);
678 values.put(Telephony.Carriers.NUMERIC, mcc + mnc);
680 String pppNumber = mPppNumber.getText();
681 if (pppNumber != null) {
682 values.put(getResources().getString(R.string.ppp_number), pppNumber);
685 if (mCurMnc != null && mCurMcc != null) {
686 if (mCurMnc.equals(mnc) && mCurMcc.equals(mcc)) {
687 values.put(Telephony.Carriers.CURRENT, 1);
691 Set<String> bearerSet = mBearerMulti.getValues();
692 int bearerBitmask = 0;
693 for (String bearer : bearerSet) {
694 if (Integer.parseInt(bearer) == 0) {
698 bearerBitmask |= ServiceState.getBitmaskForTech(Integer.parseInt(bearer));
701 values.put(Telephony.Carriers.BEARER_BITMASK, bearerBitmask);
704 if (bearerBitmask == 0 || mBearerInitialVal == 0) {
706 } else if (ServiceState.bitmaskHasTech(bearerBitmask, mBearerInitialVal)) {
707 bearerVal = mBearerInitialVal;
709 // bearer field was being used but bitmask has changed now and does not include the
710 // initial bearer value -- setting bearer to 0 but maybe better behavior is to choose a
711 // random tech from the new bitmask??
714 values.put(Telephony.Carriers.BEARER, bearerVal);
716 values.put(Telephony.Carriers.MVNO_TYPE, checkNotSet(mMvnoType.getValue()));
717 values.put(Telephony.Carriers.MVNO_MATCH_DATA, checkNotSet(mMvnoMatchData.getText()));
719 values.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled.isChecked() ? 1 : 0);
721 if (isDuplicate(values)) {
722 showDialog(DUPLICATE_DIALOG_ID);
726 getContentResolver().update(mUri, values, null, null);
731 private boolean isDuplicate(ContentValues row) {
732 if (!getResources().getBoolean(R.bool.config_enable_duplicate_apn_checking)) {
736 final Set<String> keys = row.keySet();
738 StringBuilder queryBuilder = new StringBuilder();
739 List<String> selectionArgsList = new ArrayList<>();
741 final Iterator<String> iterator = keys.iterator();
742 while (iterator.hasNext()) {
743 final String key = iterator.next();
745 if (!keyForDuplicateCheck(key) || row.getAsString(key).isEmpty()) {
746 // Skip keys which don't interest us for the duplicate query.
747 // Or if the user hasn't yet filled a field in (empty value), skip it.
751 queryBuilder.append(key);
752 queryBuilder.append("=?");
753 queryBuilder.append(" AND ");
755 selectionArgsList.add(row.getAsString(key));
757 // remove extra AND at the end
758 queryBuilder.delete(queryBuilder.length() - " AND ".length(), queryBuilder.length());
760 String[] selectionArgs = new String[selectionArgsList.size()];
761 selectionArgsList.toArray(selectionArgs);
763 try (Cursor query = getContentResolver().query(Telephony.Carriers.CONTENT_URI,
764 sProjection, queryBuilder.toString(), selectionArgs, null)) {
765 return query.getCount() > (mNewApn ? 0 : 1);
766 } catch (Exception e) {
767 Log.e(TAG, "error querying for duplicates", e);
773 * Helper method to decide what columns should be considered valid when checking for
774 * potential duplicate APNs before allowing the user to add a new one.
776 * @param key the column of the row we want to check
777 * @return whether to include this key-value pair in the duplicate query
779 private static boolean keyForDuplicateCheck(String key) {
781 case Telephony.Carriers.APN:
782 case Telephony.Carriers.MMSPROXY:
783 case Telephony.Carriers.MMSPORT:
784 case Telephony.Carriers.MMSC:
785 case Telephony.Carriers.TYPE:
786 case Telephony.Carriers.MCC:
787 case Telephony.Carriers.MNC:
788 case Telephony.Carriers.NUMERIC:
795 private String getErrorMsg() {
796 String errorMsg = null;
798 String name = checkNotSet(mName.getText());
799 String apn = checkNotSet(mApn.getText());
800 String mcc = checkNotSet(mMcc.getText());
801 String mnc = checkNotSet(mMnc.getText());
803 if (name.length() < 1) {
804 errorMsg = mRes.getString(R.string.error_name_empty);
805 } else if (apn.length() < 1) {
806 errorMsg = mRes.getString(R.string.error_apn_empty);
807 } else if (mcc.length() != 3) {
808 errorMsg = mRes.getString(R.string.error_mcc_not3);
809 } else if ((mnc.length() & 0xFFFE) != 2) {
810 errorMsg = mRes.getString(R.string.error_mnc_not23);
817 protected Dialog onCreateDialog(int id) {
819 if (id == ERROR_DIALOG_ID) {
820 String msg = getErrorMsg();
822 return new AlertDialog.Builder(this)
823 .setTitle(R.string.error_title)
824 .setPositiveButton(android.R.string.ok, null)
827 } else if (id == DUPLICATE_DIALOG_ID) {
828 return new AlertDialog.Builder(this)
829 .setTitle(R.string.duplicate_apn_error_title)
830 .setPositiveButton(android.R.string.ok, null)
831 .setMessage(getString(R.string.duplicate_apn_error_message))
835 return super.onCreateDialog(id);
839 protected void onPrepareDialog(int id, Dialog dialog) {
840 super.onPrepareDialog(id, dialog);
842 if (id == ERROR_DIALOG_ID) {
843 String msg = getErrorMsg();
846 ((AlertDialog)dialog).setMessage(msg);
851 private void deleteApn() {
852 getContentResolver().delete(mUri, null, null);
856 private String starify(String value) {
857 if (value == null || value.length() == 0) {
860 char[] password = new char[value.length()];
861 for (int i = 0; i < password.length; i++) {
864 return new String(password);
868 private String checkNull(String value) {
869 if (value == null || value.length() == 0) {
876 private String checkNotSet(String value) {
877 if (value == null || value.equals(sNotSet)) {
884 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
885 Preference pref = findPreference(key);
887 if (pref.equals(mPassword)){
888 pref.setSummary(starify(sharedPreferences.getString(key, "")));
889 } else if (pref.equals(mCarrierEnabled) || pref.equals(mBearerMulti)) {
892 pref.setSummary(checkNull(sharedPreferences.getString(key, "")));