/* * 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 static android.app.Activity.RESULT_OK; import static android.content.Context.TELEPHONY_SERVICE; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; 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.MetricsProto.MetricsEvent; 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; public class ApnEditor extends SettingsPreferenceFragment implements OnPreferenceChangeListener, OnKeyListener { private final static String TAG = ApnEditor.class.getSimpleName(); 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 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("apn_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); mAuthType.setOnPreferenceChangeListener(this); mProtocol = (ListPreference) findPreference(KEY_PROTOCOL); mProtocol.setOnPreferenceChangeListener(this); mRoamingProtocol = (ListPreference) findPreference(KEY_ROAMING_PROTOCOL); mRoamingProtocol.setOnPreferenceChangeListener(this); mCarrierEnabled = (SwitchPreference) findPreference(KEY_CARRIER_ENABLED); mBearerMulti = (MultiSelectListPreference) findPreference(KEY_BEARER_MULTI); mBearerMulti.setOnPreferenceChangeListener(this); mMvnoType = (ListPreference) findPreference(KEY_MVNO_TYPE); mMvnoType.setOnPreferenceChangeListener(this); 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; 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; } 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); } } 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 */ private 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 protected 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); } if (preference.equals(mPassword)) { preference.setSummary(starify(newValue != null ? String.valueOf(newValue) : "")); } else if (preference.equals(mCarrierEnabled) || preference.equals(mBearerMulti)) { // 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)); } } /** * 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 the data was saved */ private boolean validateAndSave(boolean force) { 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(); // Add a dummy name "Untitled", if the user exits the screen without adding a name but // entered other information worth keeping. values.put(Telephony.Carriers.NAME, name.length() < 1 ? getResources().getString(R.string.untitled_apn) : name); values.put(Telephony.Carriers.APN, apn); values.put(Telephony.Carriers.PROXY, checkNotSet(mProxy.getText())); values.put(Telephony.Carriers.PORT, checkNotSet(mPort.getText())); values.put(Telephony.Carriers.MMSPROXY, checkNotSet(mMmsProxy.getText())); values.put(Telephony.Carriers.MMSPORT, checkNotSet(mMmsPort.getText())); values.put(Telephony.Carriers.USER, checkNotSet(mUser.getText())); values.put(Telephony.Carriers.SERVER, checkNotSet(mServer.getText())); values.put(Telephony.Carriers.PASSWORD, checkNotSet(mPassword.getText())); values.put(Telephony.Carriers.MMSC, checkNotSet(mMmsc.getText())); String authVal = mAuthType.getValue(); if (authVal != null) { values.put(Telephony.Carriers.AUTH_TYPE, Integer.parseInt(authVal)); } values.put(Telephony.Carriers.PROTOCOL, checkNotSet(mProtocol.getValue())); values.put(Telephony.Carriers.ROAMING_PROTOCOL, checkNotSet(mRoamingProtocol.getValue())); values.put(Telephony.Carriers.TYPE, checkNotSet(mApnType.getText())); values.put(Telephony.Carriers.MCC, mcc); values.put(Telephony.Carriers.MNC, mnc); 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)); } } values.put(Telephony.Carriers.BEARER_BITMASK, bearerBitmask); 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; } values.put(Telephony.Carriers.BEARER, bearerVal); values.put(Telephony.Carriers.MVNO_TYPE, checkNotSet(mMvnoType.getValue())); values.put(Telephony.Carriers.MVNO_MATCH_DATA, checkNotSet(mMvnoMatchData.getText())); values.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled.isChecked() ? 1 : 0); getContentResolver().update(mUri, values, null, null); 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); } 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 DialogFragment { 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(); } } }