blob: c8f9dd12e147ae09f74156a4c029829f2d9319ae [file] [log] [blame]
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.provider.Telephony;
import android.support.v14.preference.MultiSelectListPreference;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.EditTextPreference;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.Preference.OnPreferenceChangeListener;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnKeyListener;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.ArrayUtils;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static android.app.Activity.RESULT_OK;
import static android.content.Context.TELEPHONY_SERVICE;
public class ApnEditor extends SettingsPreferenceFragment
implements OnPreferenceChangeListener, OnKeyListener {
private final static String TAG = ApnEditor.class.getSimpleName();
private final static boolean VDBG = false; // STOPSHIP if true
private final static String SAVED_POS = "pos";
private final static String KEY_AUTH_TYPE = "auth_type";
private final static String KEY_PROTOCOL = "apn_protocol";
private final static String KEY_ROAMING_PROTOCOL = "apn_roaming_protocol";
private final static String KEY_CARRIER_ENABLED = "carrier_enabled";
private final static String KEY_BEARER_MULTI = "bearer_multi";
private final static String KEY_MVNO_TYPE = "mvno_type";
private final static String KEY_PASSWORD = "apn_password";
private static final int MENU_DELETE = Menu.FIRST;
private static final int MENU_SAVE = Menu.FIRST + 1;
private static final int MENU_CANCEL = Menu.FIRST + 2;
private static String sNotSet;
private EditTextPreference mName;
private EditTextPreference mApn;
private EditTextPreference mProxy;
private EditTextPreference mPort;
private EditTextPreference mUser;
private EditTextPreference mServer;
private EditTextPreference mPassword;
private EditTextPreference mMmsc;
private EditTextPreference mMcc;
private EditTextPreference mMnc;
private EditTextPreference mMmsProxy;
private EditTextPreference mMmsPort;
private ListPreference mAuthType;
private EditTextPreference mApnType;
private ListPreference mProtocol;
private ListPreference mRoamingProtocol;
private SwitchPreference mCarrierEnabled;
private MultiSelectListPreference mBearerMulti;
private ListPreference mMvnoType;
private EditTextPreference mMvnoMatchData;
private String mCurMnc;
private String mCurMcc;
private Uri mUri;
private Cursor mCursor;
private boolean mNewApn;
private boolean mFirstTime;
private int mSubId;
private Resources mRes;
private TelephonyManager mTelephonyManager;
private int mBearerInitialVal = 0;
private String mMvnoTypeStr;
private String mMvnoMatchDataStr;
private String[] mReadOnlyApnTypes;
private String[] mReadOnlyApnFields;
private boolean mReadOnlyApn;
/**
* Standard projection for the interesting columns of a normal note.
*/
private static final String[] sProjection = new String[] {
Telephony.Carriers._ID, // 0
Telephony.Carriers.NAME, // 1
Telephony.Carriers.APN, // 2
Telephony.Carriers.PROXY, // 3
Telephony.Carriers.PORT, // 4
Telephony.Carriers.USER, // 5
Telephony.Carriers.SERVER, // 6
Telephony.Carriers.PASSWORD, // 7
Telephony.Carriers.MMSC, // 8
Telephony.Carriers.MCC, // 9
Telephony.Carriers.MNC, // 10
Telephony.Carriers.NUMERIC, // 11
Telephony.Carriers.MMSPROXY,// 12
Telephony.Carriers.MMSPORT, // 13
Telephony.Carriers.AUTH_TYPE, // 14
Telephony.Carriers.TYPE, // 15
Telephony.Carriers.PROTOCOL, // 16
Telephony.Carriers.CARRIER_ENABLED, // 17
Telephony.Carriers.BEARER, // 18
Telephony.Carriers.BEARER_BITMASK, // 19
Telephony.Carriers.ROAMING_PROTOCOL, // 20
Telephony.Carriers.MVNO_TYPE, // 21
Telephony.Carriers.MVNO_MATCH_DATA, // 22
Telephony.Carriers.EDITED // 23
};
private static final int ID_INDEX = 0;
private static final int NAME_INDEX = 1;
private static final int APN_INDEX = 2;
private static final int PROXY_INDEX = 3;
private static final int PORT_INDEX = 4;
private static final int USER_INDEX = 5;
private static final int SERVER_INDEX = 6;
private static final int PASSWORD_INDEX = 7;
private static final int MMSC_INDEX = 8;
private static final int MCC_INDEX = 9;
private static final int MNC_INDEX = 10;
private static final int MMSPROXY_INDEX = 12;
private static final int MMSPORT_INDEX = 13;
private static final int AUTH_TYPE_INDEX = 14;
private static final int TYPE_INDEX = 15;
private static final int PROTOCOL_INDEX = 16;
private static final int CARRIER_ENABLED_INDEX = 17;
private static final int BEARER_INDEX = 18;
private static final int BEARER_BITMASK_INDEX = 19;
private static final int ROAMING_PROTOCOL_INDEX = 20;
private static final int MVNO_TYPE_INDEX = 21;
private static final int MVNO_MATCH_DATA_INDEX = 22;
private static final int EDITED_INDEX = 23;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.apn_editor);
sNotSet = getResources().getString(R.string.apn_not_set);
mName = (EditTextPreference) findPreference("apn_name");
mApn = (EditTextPreference) findPreference("apn_apn");
mProxy = (EditTextPreference) findPreference("apn_http_proxy");
mPort = (EditTextPreference) findPreference("apn_http_port");
mUser = (EditTextPreference) findPreference("apn_user");
mServer = (EditTextPreference) findPreference("apn_server");
mPassword = (EditTextPreference) findPreference(KEY_PASSWORD);
mMmsProxy = (EditTextPreference) findPreference("apn_mms_proxy");
mMmsPort = (EditTextPreference) findPreference("apn_mms_port");
mMmsc = (EditTextPreference) findPreference("apn_mmsc");
mMcc = (EditTextPreference) findPreference("apn_mcc");
mMnc = (EditTextPreference) findPreference("apn_mnc");
mApnType = (EditTextPreference) findPreference("apn_type");
mAuthType = (ListPreference) findPreference(KEY_AUTH_TYPE);
mProtocol = (ListPreference) findPreference(KEY_PROTOCOL);
mRoamingProtocol = (ListPreference) findPreference(KEY_ROAMING_PROTOCOL);
mCarrierEnabled = (SwitchPreference) findPreference(KEY_CARRIER_ENABLED);
mBearerMulti = (MultiSelectListPreference) findPreference(KEY_BEARER_MULTI);
mMvnoType = (ListPreference) findPreference(KEY_MVNO_TYPE);
mMvnoMatchData = (EditTextPreference) findPreference("mvno_match_data");
mRes = getResources();
final Intent intent = getIntent();
final String action = intent.getAction();
mSubId = intent.getIntExtra(ApnSettings.SUB_ID,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
mFirstTime = icicle == null;
mReadOnlyApn = false;
mReadOnlyApnTypes = null;
mReadOnlyApnFields = null;
CarrierConfigManager configManager = (CarrierConfigManager)
getSystemService(Context.CARRIER_CONFIG_SERVICE);
if (configManager != null) {
PersistableBundle b = configManager.getConfig();
if (b != null) {
mReadOnlyApnTypes = b.getStringArray(
CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY);
mReadOnlyApnFields = b.getStringArray(
CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY);
}
}
if (action.equals(Intent.ACTION_EDIT)) {
Uri uri = intent.getData();
if (!uri.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) {
Log.e(TAG, "Edit request not for carrier table. Uri: " + uri);
finish();
return;
}
mUri = uri;
} else if (action.equals(Intent.ACTION_INSERT)) {
if (mFirstTime || icicle.getInt(SAVED_POS) == 0) {
Uri uri = intent.getData();
if (!uri.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) {
Log.e(TAG, "Insert request not for carrier table. Uri: " + uri);
finish();
return;
}
mUri = getContentResolver().insert(uri, new ContentValues());
} else {
mUri = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI,
icicle.getInt(SAVED_POS));
}
mNewApn = true;
mMvnoTypeStr = intent.getStringExtra(ApnSettings.MVNO_TYPE);
mMvnoMatchDataStr = intent.getStringExtra(ApnSettings.MVNO_MATCH_DATA);
// If we were unable to create a new note, then just finish
// this activity. A RESULT_CANCELED will be sent back to the
// original activity if they requested a result.
if (mUri == null) {
Log.w(TAG, "Failed to insert new telephony provider into "
+ getIntent().getData());
finish();
return;
}
// The new entry was created, so assume all will end well and
// set the result to be returned.
setResult(RESULT_OK, (new Intent()).setAction(mUri.toString()));
} else {
finish();
return;
}
mCursor = getActivity().managedQuery(mUri, sProjection, null, null);
mCursor.moveToFirst();
mTelephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
Log.d(TAG, "onCreate: EDITED " + mCursor.getInt(EDITED_INDEX));
// if it's not a USER_EDITED apn, check if it's read-only
if (mCursor.getInt(EDITED_INDEX) != Telephony.Carriers.USER_EDITED &&
apnTypesMatch(mReadOnlyApnTypes, mCursor.getString(TYPE_INDEX))) {
Log.d(TAG, "onCreate: apnTypesMatch; read-only APN");
mReadOnlyApn = true;
disableAllFields();
} else if (!ArrayUtils.isEmpty(mReadOnlyApnFields)) {
disableFields(mReadOnlyApnFields);
}
for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) {
getPreferenceScreen().getPreference(i).setOnPreferenceChangeListener(this);
}
fillUi();
}
/**
* Check if passed in array of APN types indicates all APN types
* @param apnTypes array of APN types. "*" indicates all types.
* @return true if all apn types are included in the array, false otherwise
*/
static boolean hasAllApns(String[] apnTypes) {
if (ArrayUtils.isEmpty(apnTypes)) {
return false;
}
List apnList = Arrays.asList(apnTypes);
if (apnList.contains(PhoneConstants.APN_TYPE_ALL)) {
Log.d(TAG, "hasAllApns: true because apnList.contains(PhoneConstants.APN_TYPE_ALL)");
return true;
}
for (String apn : PhoneConstants.APN_TYPES) {
if (!apnList.contains(apn)) {
return false;
}
}
Log.d(TAG, "hasAllApns: true");
return true;
}
/**
* Check if APN types overlap.
* @param apnTypesArray1 array of APNs. Empty array indicates no APN type; "*" indicates all
* types
* @param apnTypes2 comma separated string of APN types. Empty string represents all types.
* @return if any apn type matches return true, otherwise return false
*/
private boolean apnTypesMatch(String[] apnTypesArray1, String apnTypes2) {
if (ArrayUtils.isEmpty(apnTypesArray1)) {
return false;
}
if (hasAllApns(apnTypesArray1) || TextUtils.isEmpty(apnTypes2)) {
return true;
}
List apnTypesList1 = Arrays.asList(apnTypesArray1);
String[] apnTypesArray2 = apnTypes2.split(",");
for (String apn : apnTypesArray2) {
if (apnTypesList1.contains(apn.trim())) {
Log.d(TAG, "apnTypesMatch: true because match found for " + apn.trim());
return true;
}
}
Log.d(TAG, "apnTypesMatch: false");
return false;
}
/**
* Function to get Preference obj corresponding to an apnField
* @param apnField apn field name for which pref is needed
* @return Preference obj corresponding to passed in apnField
*/
private Preference getPreferenceFromFieldName(String apnField) {
switch (apnField) {
case Telephony.Carriers.NAME:
return mName;
case Telephony.Carriers.APN:
return mApn;
case Telephony.Carriers.PROXY:
return mProxy;
case Telephony.Carriers.PORT:
return mPort;
case Telephony.Carriers.USER:
return mUser;
case Telephony.Carriers.SERVER:
return mServer;
case Telephony.Carriers.PASSWORD:
return mPassword;
case Telephony.Carriers.MMSPROXY:
return mMmsProxy;
case Telephony.Carriers.MMSPORT:
return mMmsPort;
case Telephony.Carriers.MMSC:
return mMmsc;
case Telephony.Carriers.MCC:
return mMcc;
case Telephony.Carriers.MNC:
return mMnc;
case Telephony.Carriers.TYPE:
return mApnType;
case Telephony.Carriers.AUTH_TYPE:
return mAuthType;
case Telephony.Carriers.PROTOCOL:
return mProtocol;
case Telephony.Carriers.ROAMING_PROTOCOL:
return mRoamingProtocol;
case Telephony.Carriers.CARRIER_ENABLED:
return mCarrierEnabled;
case Telephony.Carriers.BEARER:
case Telephony.Carriers.BEARER_BITMASK:
return mBearerMulti;
case Telephony.Carriers.MVNO_TYPE:
return mMvnoType;
case Telephony.Carriers.MVNO_MATCH_DATA:
return mMvnoMatchData;
}
return null;
}
/**
* Disables given fields so that user cannot modify them
*
* @param apnFields fields to be disabled
*/
private void disableFields(String[] apnFields) {
for (String apnField : apnFields) {
Preference preference = getPreferenceFromFieldName(apnField);
if (preference != null) {
preference.setEnabled(false);
}
}
}
/**
* Disables all fields so that user cannot modify the APN
*/
private void disableAllFields() {
mName.setEnabled(false);
mApn.setEnabled(false);
mProxy.setEnabled(false);
mPort.setEnabled(false);
mUser.setEnabled(false);
mServer.setEnabled(false);
mPassword.setEnabled(false);
mMmsProxy.setEnabled(false);
mMmsPort.setEnabled(false);
mMmsc.setEnabled(false);
mMcc.setEnabled(false);
mMnc.setEnabled(false);
mApnType.setEnabled(false);
mAuthType.setEnabled(false);
mProtocol.setEnabled(false);
mRoamingProtocol.setEnabled(false);
mCarrierEnabled.setEnabled(false);
mBearerMulti.setEnabled(false);
mMvnoType.setEnabled(false);
mMvnoMatchData.setEnabled(false);
}
@Override
public int getMetricsCategory() {
return MetricsEvent.APN_EDITOR;
}
@Override
public void onResume() {
super.onResume();
if (mUri == null && mNewApn) {
// The URI could have been deleted when activity is paused,
// therefore, it needs to be restored.
mUri = getContentResolver().insert(getIntent().getData(), new ContentValues());
if (mUri == null) {
Log.w(TAG, "Failed to insert new telephony provider into "
+ getIntent().getData());
finish();
return;
}
mCursor = getActivity().managedQuery(mUri, sProjection, null, null);
mCursor.moveToFirst();
}
}
@Override
public void onPause() {
super.onPause();
}
private void fillUi() {
if (mFirstTime) {
mFirstTime = false;
// Fill in all the values from the db in both text editor and summary
mName.setText(mCursor.getString(NAME_INDEX));
mApn.setText(mCursor.getString(APN_INDEX));
mProxy.setText(mCursor.getString(PROXY_INDEX));
mPort.setText(mCursor.getString(PORT_INDEX));
mUser.setText(mCursor.getString(USER_INDEX));
mServer.setText(mCursor.getString(SERVER_INDEX));
mPassword.setText(mCursor.getString(PASSWORD_INDEX));
mMmsProxy.setText(mCursor.getString(MMSPROXY_INDEX));
mMmsPort.setText(mCursor.getString(MMSPORT_INDEX));
mMmsc.setText(mCursor.getString(MMSC_INDEX));
mMcc.setText(mCursor.getString(MCC_INDEX));
mMnc.setText(mCursor.getString(MNC_INDEX));
mApnType.setText(mCursor.getString(TYPE_INDEX));
if (mNewApn) {
String numeric = mTelephonyManager.getSimOperator(mSubId);
// MCC is first 3 chars and then in 2 - 3 chars of MNC
if (numeric != null && numeric.length() > 4) {
// Country code
String mcc = numeric.substring(0, 3);
// Network code
String mnc = numeric.substring(3);
// Auto populate MNC and MCC for new entries, based on what SIM reports
mMcc.setText(mcc);
mMnc.setText(mnc);
mCurMnc = mnc;
mCurMcc = mcc;
}
}
int authVal = mCursor.getInt(AUTH_TYPE_INDEX);
if (authVal != -1) {
mAuthType.setValueIndex(authVal);
} else {
mAuthType.setValue(null);
}
mProtocol.setValue(mCursor.getString(PROTOCOL_INDEX));
mRoamingProtocol.setValue(mCursor.getString(ROAMING_PROTOCOL_INDEX));
mCarrierEnabled.setChecked(mCursor.getInt(CARRIER_ENABLED_INDEX)==1);
mBearerInitialVal = mCursor.getInt(BEARER_INDEX);
HashSet<String> bearers = new HashSet<String>();
int bearerBitmask = mCursor.getInt(BEARER_BITMASK_INDEX);
if (bearerBitmask == 0) {
if (mBearerInitialVal == 0) {
bearers.add("" + 0);
}
} else {
int i = 1;
while (bearerBitmask != 0) {
if ((bearerBitmask & 1) == 1) {
bearers.add("" + i);
}
bearerBitmask >>= 1;
i++;
}
}
if (mBearerInitialVal != 0 && bearers.contains("" + mBearerInitialVal) == false) {
// add mBearerInitialVal to bearers
bearers.add("" + mBearerInitialVal);
}
mBearerMulti.setValues(bearers);
mMvnoType.setValue(mCursor.getString(MVNO_TYPE_INDEX));
mMvnoMatchData.setEnabled(false);
mMvnoMatchData.setText(mCursor.getString(MVNO_MATCH_DATA_INDEX));
if (mNewApn && mMvnoTypeStr != null && mMvnoMatchDataStr != null) {
mMvnoType.setValue(mMvnoTypeStr);
mMvnoMatchData.setText(mMvnoMatchDataStr);
}
}
mName.setSummary(checkNull(mName.getText()));
mApn.setSummary(checkNull(mApn.getText()));
mProxy.setSummary(checkNull(mProxy.getText()));
mPort.setSummary(checkNull(mPort.getText()));
mUser.setSummary(checkNull(mUser.getText()));
mServer.setSummary(checkNull(mServer.getText()));
mPassword.setSummary(starify(mPassword.getText()));
mMmsProxy.setSummary(checkNull(mMmsProxy.getText()));
mMmsPort.setSummary(checkNull(mMmsPort.getText()));
mMmsc.setSummary(checkNull(mMmsc.getText()));
mMcc.setSummary(checkNull(mMcc.getText()));
mMnc.setSummary(checkNull(mMnc.getText()));
mApnType.setSummary(checkNull(mApnType.getText()));
String authVal = mAuthType.getValue();
if (authVal != null) {
int authValIndex = Integer.parseInt(authVal);
mAuthType.setValueIndex(authValIndex);
String []values = mRes.getStringArray(R.array.apn_auth_entries);
mAuthType.setSummary(values[authValIndex]);
} else {
mAuthType.setSummary(sNotSet);
}
mProtocol.setSummary(checkNull(protocolDescription(mProtocol.getValue(), mProtocol)));
mRoamingProtocol.setSummary(
checkNull(protocolDescription(mRoamingProtocol.getValue(), mRoamingProtocol)));
mBearerMulti.setSummary(
checkNull(bearerMultiDescription(mBearerMulti.getValues())));
mMvnoType.setSummary(
checkNull(mvnoDescription(mMvnoType.getValue())));
mMvnoMatchData.setSummary(checkNull(mMvnoMatchData.getText()));
// allow user to edit carrier_enabled for some APN
boolean ceEditable = getResources().getBoolean(R.bool.config_allow_edit_carrier_enabled);
if (ceEditable) {
mCarrierEnabled.setEnabled(true);
} else {
mCarrierEnabled.setEnabled(false);
}
}
/**
* Returns the UI choice (e.g., "IPv4/IPv6") corresponding to the given
* raw value of the protocol preference (e.g., "IPV4V6"). If unknown,
* return null.
*/
private String protocolDescription(String raw, ListPreference protocol) {
int protocolIndex = protocol.findIndexOfValue(raw);
if (protocolIndex == -1) {
return null;
} else {
String[] values = mRes.getStringArray(R.array.apn_protocol_entries);
try {
return values[protocolIndex];
} catch (ArrayIndexOutOfBoundsException e) {
return null;
}
}
}
private String bearerDescription(String raw) {
int mBearerIndex = mBearerMulti.findIndexOfValue(raw);
if (mBearerIndex == -1) {
return null;
} else {
String[] values = mRes.getStringArray(R.array.bearer_entries);
try {
return values[mBearerIndex];
} catch (ArrayIndexOutOfBoundsException e) {
return null;
}
}
}
private String bearerMultiDescription(Set<String> raw) {
String[] values = mRes.getStringArray(R.array.bearer_entries);
StringBuilder retVal = new StringBuilder();
boolean first = true;
for (String bearer : raw) {
int bearerIndex = mBearerMulti.findIndexOfValue(bearer);
try {
if (first) {
retVal.append(values[bearerIndex]);
first = false;
} else {
retVal.append(", " + values[bearerIndex]);
}
} catch (ArrayIndexOutOfBoundsException e) {
// ignore
}
}
String val = retVal.toString();
if (!TextUtils.isEmpty(val)) {
return val;
}
return null;
}
private String mvnoDescription(String newValue) {
int mvnoIndex = mMvnoType.findIndexOfValue(newValue);
String oldValue = mMvnoType.getValue();
if (mvnoIndex == -1) {
return null;
} else {
String[] values = mRes.getStringArray(R.array.mvno_type_entries);
mMvnoMatchData.setEnabled(mvnoIndex != 0);
if (newValue != null && newValue.equals(oldValue) == false) {
if (values[mvnoIndex].equals("SPN")) {
mMvnoMatchData.setText(mTelephonyManager.getSimOperatorName());
} else if (values[mvnoIndex].equals("IMSI")) {
String numeric = mTelephonyManager.getSimOperator(mSubId);
mMvnoMatchData.setText(numeric + "x");
} else if (values[mvnoIndex].equals("GID")) {
mMvnoMatchData.setText(mTelephonyManager.getGroupIdLevel1());
}
}
try {
return values[mvnoIndex];
} catch (ArrayIndexOutOfBoundsException e) {
return null;
}
}
}
public boolean onPreferenceChange(Preference preference, Object newValue) {
String key = preference.getKey();
if (KEY_AUTH_TYPE.equals(key)) {
try {
int index = Integer.parseInt((String) newValue);
mAuthType.setValueIndex(index);
String[] values = mRes.getStringArray(R.array.apn_auth_entries);
mAuthType.setSummary(values[index]);
} catch (NumberFormatException e) {
return false;
}
} else if (KEY_PROTOCOL.equals(key)) {
String protocol = protocolDescription((String) newValue, mProtocol);
if (protocol == null) {
return false;
}
mProtocol.setSummary(protocol);
mProtocol.setValue((String) newValue);
} else if (KEY_ROAMING_PROTOCOL.equals(key)) {
String protocol = protocolDescription((String) newValue, mRoamingProtocol);
if (protocol == null) {
return false;
}
mRoamingProtocol.setSummary(protocol);
mRoamingProtocol.setValue((String) newValue);
} else if (KEY_BEARER_MULTI.equals(key)) {
String bearer = bearerMultiDescription((Set<String>) newValue);
if (bearer == null) {
return false;
}
mBearerMulti.setValues((Set<String>) newValue);
mBearerMulti.setSummary(bearer);
} else if (KEY_MVNO_TYPE.equals(key)) {
String mvno = mvnoDescription((String) newValue);
if (mvno == null) {
return false;
}
mMvnoType.setValue((String) newValue);
mMvnoType.setSummary(mvno);
} else if (KEY_PASSWORD.equals(key)) {
mPassword.setSummary(starify(newValue != null ? String.valueOf(newValue) : ""));
} else if (KEY_CARRIER_ENABLED.equals(key)) {
// do nothing
} else {
preference.setSummary(checkNull(newValue != null ? String.valueOf(newValue) : null));
}
return true;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
// If it's a new APN, then cancel will delete the new entry in onPause
if (!mNewApn && !mReadOnlyApn) {
menu.add(0, MENU_DELETE, 0, R.string.menu_delete)
.setIcon(R.drawable.ic_menu_delete);
}
menu.add(0, MENU_SAVE, 0, R.string.menu_save)
.setIcon(android.R.drawable.ic_menu_save);
menu.add(0, MENU_CANCEL, 0, R.string.menu_cancel)
.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_DELETE:
deleteApn();
return true;
case MENU_SAVE:
if (validateAndSave(false)) {
finish();
}
return true;
case MENU_CANCEL:
if (mNewApn) {
getContentResolver().delete(mUri, null, null);
}
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.setOnKeyListener(this);
view.setFocusableInTouchMode(true);
view.requestFocus();
}
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() != KeyEvent.ACTION_DOWN) return false;
switch (keyCode) {
case KeyEvent.KEYCODE_BACK: {
if (validateAndSave(false)) {
finish();
}
return true;
}
}
return false;
}
@Override
public void onSaveInstanceState(Bundle icicle) {
super.onSaveInstanceState(icicle);
if (validateAndSave(true)) {
icicle.putInt(SAVED_POS, mCursor.getInt(ID_INDEX));
}
}
/**
* Add key, value to cv and compare the value against the value at index in mCursor. Return true
* if values are different. assumeDiff indicates if values can be assumed different in which
* case no comparison is needed.
* @return true if value is different from the value at index in mCursor
*/
boolean setStringValueAndCheckIfDiff(ContentValues cv, String key, String value,
boolean assumeDiff, int index) {
cv.put(key, value);
String valueFromCursor = mCursor.getString(index);
if (VDBG) {
Log.d(TAG, "setStringValueAndCheckIfDiff: assumeDiff: " + assumeDiff
+ " key: " + key
+ " value: '" + value
+ "' valueFromCursor: '" + valueFromCursor + "'");
}
return assumeDiff
|| !((TextUtils.isEmpty(value) && TextUtils.isEmpty(valueFromCursor))
|| (value != null && value.equals(valueFromCursor)));
}
/**
* Add key, value to cv and compare the value against the value at index in mCursor. Return true
* if values are different. assumeDiff indicates if values can be assumed different in which
* case no comparison is needed.
* @return true if value is different from the value at index in mCursor
*/
boolean setIntValueAndCheckIfDiff(ContentValues cv, String key, int value,
boolean assumeDiff, int index) {
cv.put(key, value);
int valueFromCursor = mCursor.getInt(index);
if (VDBG) {
Log.d(TAG, "setIntValueAndCheckIfDiff: assumeDiff: " + assumeDiff
+ " key: " + key
+ " value: '" + value
+ "' valueFromCursor: '" + valueFromCursor + "'");
}
return assumeDiff || value != valueFromCursor;
}
/**
* Check the key fields' validity and save if valid.
* @param force save even if the fields are not valid, if the app is
* being suspended
* @return true if there's no error
*/
private boolean validateAndSave(boolean force) {
// nothing to do if it's a read only APN
if (mReadOnlyApn) {
return true;
}
String name = checkNotSet(mName.getText());
String apn = checkNotSet(mApn.getText());
String mcc = checkNotSet(mMcc.getText());
String mnc = checkNotSet(mMnc.getText());
if (getErrorMsg() != null && !force) {
ErrorDialog.showError(this);
return false;
}
if (!mCursor.moveToFirst()) {
Log.w(TAG,
"Could not go to the first row in the Cursor when saving data.");
return false;
}
// If it's a new APN and a name or apn haven't been entered, then erase the entry
if (force && mNewApn && name.length() < 1 && apn.length() < 1) {
getContentResolver().delete(mUri, null, null);
mUri = null;
return false;
}
ContentValues values = new ContentValues();
// call update() if it's a new APN. If not, check if any field differs from the db value;
// if any diff is found update() should be called
boolean callUpdate = mNewApn;
// Add a dummy name "Untitled", if the user exits the screen without adding a name but
// entered other information worth keeping.
callUpdate = setStringValueAndCheckIfDiff(values,
Telephony.Carriers.NAME,
name.length() < 1 ? getResources().getString(R.string.untitled_apn) : name,
callUpdate,
NAME_INDEX);
callUpdate = setStringValueAndCheckIfDiff(values,
Telephony.Carriers.APN,
apn,
callUpdate,
APN_INDEX);
callUpdate = setStringValueAndCheckIfDiff(values,
Telephony.Carriers.PROXY,
checkNotSet(mProxy.getText()),
callUpdate,
PROXY_INDEX);
callUpdate = setStringValueAndCheckIfDiff(values,
Telephony.Carriers.PORT,
checkNotSet(mPort.getText()),
callUpdate,
PORT_INDEX);
callUpdate = setStringValueAndCheckIfDiff(values,
Telephony.Carriers.MMSPROXY,
checkNotSet(mMmsProxy.getText()),
callUpdate,
MMSPROXY_INDEX);
callUpdate = setStringValueAndCheckIfDiff(values,
Telephony.Carriers.MMSPORT,
checkNotSet(mMmsPort.getText()),
callUpdate,
MMSPORT_INDEX);
callUpdate = setStringValueAndCheckIfDiff(values,
Telephony.Carriers.USER,
checkNotSet(mUser.getText()),
callUpdate,
USER_INDEX);
callUpdate = setStringValueAndCheckIfDiff(values,
Telephony.Carriers.SERVER,
checkNotSet(mServer.getText()),
callUpdate,
SERVER_INDEX);
callUpdate = setStringValueAndCheckIfDiff(values,
Telephony.Carriers.PASSWORD,
checkNotSet(mPassword.getText()),
callUpdate,
PASSWORD_INDEX);
callUpdate = setStringValueAndCheckIfDiff(values,
Telephony.Carriers.MMSC,
checkNotSet(mMmsc.getText()),
callUpdate,
MMSC_INDEX);
String authVal = mAuthType.getValue();
if (authVal != null) {
callUpdate = setIntValueAndCheckIfDiff(values,
Telephony.Carriers.AUTH_TYPE,
Integer.parseInt(authVal),
callUpdate,
AUTH_TYPE_INDEX);
}
callUpdate = setStringValueAndCheckIfDiff(values,
Telephony.Carriers.PROTOCOL,
checkNotSet(mProtocol.getValue()),
callUpdate,
PROTOCOL_INDEX);
callUpdate = setStringValueAndCheckIfDiff(values,
Telephony.Carriers.ROAMING_PROTOCOL,
checkNotSet(mRoamingProtocol.getValue()),
callUpdate,
ROAMING_PROTOCOL_INDEX);
callUpdate = setStringValueAndCheckIfDiff(values,
Telephony.Carriers.TYPE,
checkNotSet(mApnType.getText()),
callUpdate,
TYPE_INDEX);
callUpdate = setStringValueAndCheckIfDiff(values,
Telephony.Carriers.MCC,
mcc,
callUpdate,
MCC_INDEX);
callUpdate = setStringValueAndCheckIfDiff(values,
Telephony.Carriers.MNC,
mnc,
callUpdate,
MNC_INDEX);
values.put(Telephony.Carriers.NUMERIC, mcc + mnc);
if (mCurMnc != null && mCurMcc != null) {
if (mCurMnc.equals(mnc) && mCurMcc.equals(mcc)) {
values.put(Telephony.Carriers.CURRENT, 1);
}
}
Set<String> bearerSet = mBearerMulti.getValues();
int bearerBitmask = 0;
for (String bearer : bearerSet) {
if (Integer.parseInt(bearer) == 0) {
bearerBitmask = 0;
break;
} else {
bearerBitmask |= ServiceState.getBitmaskForTech(Integer.parseInt(bearer));
}
}
callUpdate = setIntValueAndCheckIfDiff(values,
Telephony.Carriers.BEARER_BITMASK,
bearerBitmask,
callUpdate,
BEARER_BITMASK_INDEX);
int bearerVal;
if (bearerBitmask == 0 || mBearerInitialVal == 0) {
bearerVal = 0;
} else if (ServiceState.bitmaskHasTech(bearerBitmask, mBearerInitialVal)) {
bearerVal = mBearerInitialVal;
} else {
// bearer field was being used but bitmask has changed now and does not include the
// initial bearer value -- setting bearer to 0 but maybe better behavior is to choose a
// random tech from the new bitmask??
bearerVal = 0;
}
callUpdate = setIntValueAndCheckIfDiff(values,
Telephony.Carriers.BEARER,
bearerVal,
callUpdate,
BEARER_INDEX);
callUpdate = setStringValueAndCheckIfDiff(values,
Telephony.Carriers.MVNO_TYPE,
checkNotSet(mMvnoType.getValue()),
callUpdate,
MVNO_TYPE_INDEX);
callUpdate = setStringValueAndCheckIfDiff(values,
Telephony.Carriers.MVNO_MATCH_DATA,
checkNotSet(mMvnoMatchData.getText()),
callUpdate,
MVNO_MATCH_DATA_INDEX);
callUpdate = setIntValueAndCheckIfDiff(values,
Telephony.Carriers.CARRIER_ENABLED,
mCarrierEnabled.isChecked() ? 1 : 0,
callUpdate,
CARRIER_ENABLED_INDEX);
if (callUpdate) {
getContentResolver().update(mUri, values, null, null);
} else {
if (VDBG) Log.d(TAG, "validateAndSave: not calling update()");
}
return true;
}
private String getErrorMsg() {
String errorMsg = null;
String name = checkNotSet(mName.getText());
String apn = checkNotSet(mApn.getText());
String mcc = checkNotSet(mMcc.getText());
String mnc = checkNotSet(mMnc.getText());
if (name.length() < 1) {
errorMsg = mRes.getString(R.string.error_name_empty);
} else if (apn.length() < 1) {
errorMsg = mRes.getString(R.string.error_apn_empty);
} else if (mcc.length() != 3) {
errorMsg = mRes.getString(R.string.error_mcc_not3);
} else if ((mnc.length() & 0xFFFE) != 2) {
errorMsg = mRes.getString(R.string.error_mnc_not23);
}
if (errorMsg == null) {
// if carrier does not allow editing certain apn types, make sure type does not include
// those
if (!ArrayUtils.isEmpty(mReadOnlyApnTypes)
&& apnTypesMatch(mReadOnlyApnTypes, mApnType.getText())) {
StringBuilder stringBuilder = new StringBuilder();
for (String type : mReadOnlyApnTypes) {
stringBuilder.append(type).append(", ");
}
// remove last ", "
if (stringBuilder.length() >= 2) {
stringBuilder.delete(stringBuilder.length() - 2, stringBuilder.length());
}
errorMsg = String.format(mRes.getString(R.string.error_adding_apn_type),
stringBuilder);
}
}
return errorMsg;
}
private void deleteApn() {
getContentResolver().delete(mUri, null, null);
finish();
}
private String starify(String value) {
if (value == null || value.length() == 0) {
return sNotSet;
} else {
char[] password = new char[value.length()];
for (int i = 0; i < password.length; i++) {
password[i] = '*';
}
return new String(password);
}
}
private String checkNull(String value) {
if (value == null || value.length() == 0) {
return sNotSet;
} else {
return value;
}
}
private String checkNotSet(String value) {
if (value == null || value.equals(sNotSet)) {
return "";
} else {
return value;
}
}
public static class ErrorDialog extends InstrumentedDialogFragment {
public static void showError(ApnEditor editor) {
ErrorDialog dialog = new ErrorDialog();
dialog.setTargetFragment(editor, 0);
dialog.show(editor.getFragmentManager(), "error");
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
String msg = ((ApnEditor) getTargetFragment()).getErrorMsg();
return new AlertDialog.Builder(getContext())
.setTitle(R.string.error_title)
.setPositiveButton(android.R.string.ok, null)
.setMessage(msg)
.create();
}
@Override
public int getMetricsCategory() {
return MetricsEvent.DIALOG_APN_EDITOR_ERROR;
}
}
}