| /* |
| * 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; |
| } |
| } |
| |
| } |