Home | History | Annotate | Download | only in settings
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.settings;
     18 
     19 import static android.app.Activity.RESULT_OK;
     20 import static android.content.Context.TELEPHONY_SERVICE;
     21 
     22 import android.app.AlertDialog;
     23 import android.app.Dialog;
     24 import android.app.DialogFragment;
     25 import android.content.ContentUris;
     26 import android.content.ContentValues;
     27 import android.content.Intent;
     28 import android.content.res.Resources;
     29 import android.database.Cursor;
     30 import android.net.Uri;
     31 import android.os.Bundle;
     32 import android.provider.Telephony;
     33 import android.support.v14.preference.MultiSelectListPreference;
     34 import android.support.v14.preference.SwitchPreference;
     35 import android.support.v7.preference.EditTextPreference;
     36 import android.support.v7.preference.ListPreference;
     37 import android.support.v7.preference.Preference;
     38 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
     39 import android.telephony.ServiceState;
     40 import android.telephony.SubscriptionManager;
     41 import android.telephony.TelephonyManager;
     42 import android.text.TextUtils;
     43 import android.util.Log;
     44 import android.view.KeyEvent;
     45 import android.view.Menu;
     46 import android.view.MenuInflater;
     47 import android.view.MenuItem;
     48 import android.view.View;
     49 import android.view.View.OnKeyListener;
     50 
     51 import com.android.internal.logging.MetricsProto.MetricsEvent;
     52 
     53 import java.util.HashSet;
     54 import java.util.Set;
     55 
     56 public class ApnEditor extends SettingsPreferenceFragment
     57         implements OnPreferenceChangeListener, OnKeyListener {
     58 
     59     private final static String TAG = ApnEditor.class.getSimpleName();
     60 
     61     private final static String SAVED_POS = "pos";
     62     private final static String KEY_AUTH_TYPE = "auth_type";
     63     private final static String KEY_PROTOCOL = "apn_protocol";
     64     private final static String KEY_ROAMING_PROTOCOL = "apn_roaming_protocol";
     65     private final static String KEY_CARRIER_ENABLED = "carrier_enabled";
     66     private final static String KEY_BEARER_MULTI = "bearer_multi";
     67     private final static String KEY_MVNO_TYPE = "mvno_type";
     68 
     69     private static final int MENU_DELETE = Menu.FIRST;
     70     private static final int MENU_SAVE = Menu.FIRST + 1;
     71     private static final int MENU_CANCEL = Menu.FIRST + 2;
     72 
     73     private static String sNotSet;
     74     private EditTextPreference mName;
     75     private EditTextPreference mApn;
     76     private EditTextPreference mProxy;
     77     private EditTextPreference mPort;
     78     private EditTextPreference mUser;
     79     private EditTextPreference mServer;
     80     private EditTextPreference mPassword;
     81     private EditTextPreference mMmsc;
     82     private EditTextPreference mMcc;
     83     private EditTextPreference mMnc;
     84     private EditTextPreference mMmsProxy;
     85     private EditTextPreference mMmsPort;
     86     private ListPreference mAuthType;
     87     private EditTextPreference mApnType;
     88     private ListPreference mProtocol;
     89     private ListPreference mRoamingProtocol;
     90     private SwitchPreference mCarrierEnabled;
     91     private MultiSelectListPreference mBearerMulti;
     92     private ListPreference mMvnoType;
     93     private EditTextPreference mMvnoMatchData;
     94 
     95     private String mCurMnc;
     96     private String mCurMcc;
     97 
     98     private Uri mUri;
     99     private Cursor mCursor;
    100     private boolean mNewApn;
    101     private boolean mFirstTime;
    102     private int mSubId;
    103     private Resources mRes;
    104     private TelephonyManager mTelephonyManager;
    105     private int mBearerInitialVal = 0;
    106     private String mMvnoTypeStr;
    107     private String mMvnoMatchDataStr;
    108 
    109     /**
    110      * Standard projection for the interesting columns of a normal note.
    111      */
    112     private static final String[] sProjection = new String[] {
    113             Telephony.Carriers._ID,     // 0
    114             Telephony.Carriers.NAME,    // 1
    115             Telephony.Carriers.APN,     // 2
    116             Telephony.Carriers.PROXY,   // 3
    117             Telephony.Carriers.PORT,    // 4
    118             Telephony.Carriers.USER,    // 5
    119             Telephony.Carriers.SERVER,  // 6
    120             Telephony.Carriers.PASSWORD, // 7
    121             Telephony.Carriers.MMSC, // 8
    122             Telephony.Carriers.MCC, // 9
    123             Telephony.Carriers.MNC, // 10
    124             Telephony.Carriers.NUMERIC, // 11
    125             Telephony.Carriers.MMSPROXY,// 12
    126             Telephony.Carriers.MMSPORT, // 13
    127             Telephony.Carriers.AUTH_TYPE, // 14
    128             Telephony.Carriers.TYPE, // 15
    129             Telephony.Carriers.PROTOCOL, // 16
    130             Telephony.Carriers.CARRIER_ENABLED, // 17
    131             Telephony.Carriers.BEARER, // 18
    132             Telephony.Carriers.BEARER_BITMASK, // 19
    133             Telephony.Carriers.ROAMING_PROTOCOL, // 20
    134             Telephony.Carriers.MVNO_TYPE,   // 21
    135             Telephony.Carriers.MVNO_MATCH_DATA  // 22
    136     };
    137 
    138     private static final int ID_INDEX = 0;
    139     private static final int NAME_INDEX = 1;
    140     private static final int APN_INDEX = 2;
    141     private static final int PROXY_INDEX = 3;
    142     private static final int PORT_INDEX = 4;
    143     private static final int USER_INDEX = 5;
    144     private static final int SERVER_INDEX = 6;
    145     private static final int PASSWORD_INDEX = 7;
    146     private static final int MMSC_INDEX = 8;
    147     private static final int MCC_INDEX = 9;
    148     private static final int MNC_INDEX = 10;
    149     private static final int MMSPROXY_INDEX = 12;
    150     private static final int MMSPORT_INDEX = 13;
    151     private static final int AUTH_TYPE_INDEX = 14;
    152     private static final int TYPE_INDEX = 15;
    153     private static final int PROTOCOL_INDEX = 16;
    154     private static final int CARRIER_ENABLED_INDEX = 17;
    155     private static final int BEARER_INDEX = 18;
    156     private static final int BEARER_BITMASK_INDEX = 19;
    157     private static final int ROAMING_PROTOCOL_INDEX = 20;
    158     private static final int MVNO_TYPE_INDEX = 21;
    159     private static final int MVNO_MATCH_DATA_INDEX = 22;
    160 
    161 
    162     @Override
    163     public void onCreate(Bundle icicle) {
    164         super.onCreate(icicle);
    165 
    166         addPreferencesFromResource(R.xml.apn_editor);
    167 
    168         sNotSet = getResources().getString(R.string.apn_not_set);
    169         mName = (EditTextPreference) findPreference("apn_name");
    170         mApn = (EditTextPreference) findPreference("apn_apn");
    171         mProxy = (EditTextPreference) findPreference("apn_http_proxy");
    172         mPort = (EditTextPreference) findPreference("apn_http_port");
    173         mUser = (EditTextPreference) findPreference("apn_user");
    174         mServer = (EditTextPreference) findPreference("apn_server");
    175         mPassword = (EditTextPreference) findPreference("apn_password");
    176         mMmsProxy = (EditTextPreference) findPreference("apn_mms_proxy");
    177         mMmsPort = (EditTextPreference) findPreference("apn_mms_port");
    178         mMmsc = (EditTextPreference) findPreference("apn_mmsc");
    179         mMcc = (EditTextPreference) findPreference("apn_mcc");
    180         mMnc = (EditTextPreference) findPreference("apn_mnc");
    181         mApnType = (EditTextPreference) findPreference("apn_type");
    182 
    183         mAuthType = (ListPreference) findPreference(KEY_AUTH_TYPE);
    184         mAuthType.setOnPreferenceChangeListener(this);
    185 
    186         mProtocol = (ListPreference) findPreference(KEY_PROTOCOL);
    187         mProtocol.setOnPreferenceChangeListener(this);
    188 
    189         mRoamingProtocol = (ListPreference) findPreference(KEY_ROAMING_PROTOCOL);
    190         mRoamingProtocol.setOnPreferenceChangeListener(this);
    191 
    192         mCarrierEnabled = (SwitchPreference) findPreference(KEY_CARRIER_ENABLED);
    193 
    194         mBearerMulti = (MultiSelectListPreference) findPreference(KEY_BEARER_MULTI);
    195         mBearerMulti.setOnPreferenceChangeListener(this);
    196 
    197         mMvnoType = (ListPreference) findPreference(KEY_MVNO_TYPE);
    198         mMvnoType.setOnPreferenceChangeListener(this);
    199         mMvnoMatchData = (EditTextPreference) findPreference("mvno_match_data");
    200 
    201         mRes = getResources();
    202 
    203         final Intent intent = getIntent();
    204         final String action = intent.getAction();
    205         mSubId = intent.getIntExtra(ApnSettings.SUB_ID,
    206                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
    207 
    208         mFirstTime = icicle == null;
    209 
    210         if (action.equals(Intent.ACTION_EDIT)) {
    211             Uri uri = intent.getData();
    212             if (!uri.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) {
    213                 Log.e(TAG, "Edit request not for carrier table. Uri: " + uri);
    214                 finish();
    215                 return;
    216             }
    217             mUri = uri;
    218         } else if (action.equals(Intent.ACTION_INSERT)) {
    219             if (mFirstTime || icicle.getInt(SAVED_POS) == 0) {
    220                 Uri uri = intent.getData();
    221                 if (!uri.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) {
    222                     Log.e(TAG, "Insert request not for carrier table. Uri: " + uri);
    223                     finish();
    224                     return;
    225                 }
    226                 mUri = getContentResolver().insert(uri, new ContentValues());
    227             } else {
    228                 mUri = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI,
    229                         icicle.getInt(SAVED_POS));
    230             }
    231             mNewApn = true;
    232             mMvnoTypeStr = intent.getStringExtra(ApnSettings.MVNO_TYPE);
    233             mMvnoMatchDataStr = intent.getStringExtra(ApnSettings.MVNO_MATCH_DATA);
    234             // If we were unable to create a new note, then just finish
    235             // this activity.  A RESULT_CANCELED will be sent back to the
    236             // original activity if they requested a result.
    237             if (mUri == null) {
    238                 Log.w(TAG, "Failed to insert new telephony provider into "
    239                         + getIntent().getData());
    240                 finish();
    241                 return;
    242             }
    243 
    244             // The new entry was created, so assume all will end well and
    245             // set the result to be returned.
    246             setResult(RESULT_OK, (new Intent()).setAction(mUri.toString()));
    247 
    248         } else {
    249             finish();
    250             return;
    251         }
    252 
    253         mCursor = getActivity().managedQuery(mUri, sProjection, null, null);
    254         mCursor.moveToFirst();
    255 
    256         mTelephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
    257 
    258         for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) {
    259             getPreferenceScreen().getPreference(i).setOnPreferenceChangeListener(this);
    260         }
    261 
    262         fillUi();
    263     }
    264 
    265     @Override
    266     protected int getMetricsCategory() {
    267         return MetricsEvent.APN_EDITOR;
    268     }
    269 
    270     @Override
    271     public void onResume() {
    272         super.onResume();
    273     }
    274 
    275     @Override
    276     public void onPause() {
    277         super.onPause();
    278     }
    279 
    280     private void fillUi() {
    281         if (mFirstTime) {
    282             mFirstTime = false;
    283             // Fill in all the values from the db in both text editor and summary
    284             mName.setText(mCursor.getString(NAME_INDEX));
    285             mApn.setText(mCursor.getString(APN_INDEX));
    286             mProxy.setText(mCursor.getString(PROXY_INDEX));
    287             mPort.setText(mCursor.getString(PORT_INDEX));
    288             mUser.setText(mCursor.getString(USER_INDEX));
    289             mServer.setText(mCursor.getString(SERVER_INDEX));
    290             mPassword.setText(mCursor.getString(PASSWORD_INDEX));
    291             mMmsProxy.setText(mCursor.getString(MMSPROXY_INDEX));
    292             mMmsPort.setText(mCursor.getString(MMSPORT_INDEX));
    293             mMmsc.setText(mCursor.getString(MMSC_INDEX));
    294             mMcc.setText(mCursor.getString(MCC_INDEX));
    295             mMnc.setText(mCursor.getString(MNC_INDEX));
    296             mApnType.setText(mCursor.getString(TYPE_INDEX));
    297             if (mNewApn) {
    298                 String numeric = mTelephonyManager.getSimOperator(mSubId);
    299                 // MCC is first 3 chars and then in 2 - 3 chars of MNC
    300                 if (numeric != null && numeric.length() > 4) {
    301                     // Country code
    302                     String mcc = numeric.substring(0, 3);
    303                     // Network code
    304                     String mnc = numeric.substring(3);
    305                     // Auto populate MNC and MCC for new entries, based on what SIM reports
    306                     mMcc.setText(mcc);
    307                     mMnc.setText(mnc);
    308                     mCurMnc = mnc;
    309                     mCurMcc = mcc;
    310                 }
    311             }
    312             int authVal = mCursor.getInt(AUTH_TYPE_INDEX);
    313             if (authVal != -1) {
    314                 mAuthType.setValueIndex(authVal);
    315             } else {
    316                 mAuthType.setValue(null);
    317             }
    318 
    319             mProtocol.setValue(mCursor.getString(PROTOCOL_INDEX));
    320             mRoamingProtocol.setValue(mCursor.getString(ROAMING_PROTOCOL_INDEX));
    321             mCarrierEnabled.setChecked(mCursor.getInt(CARRIER_ENABLED_INDEX)==1);
    322             mBearerInitialVal = mCursor.getInt(BEARER_INDEX);
    323 
    324             HashSet<String> bearers = new HashSet<String>();
    325             int bearerBitmask = mCursor.getInt(BEARER_BITMASK_INDEX);
    326             if (bearerBitmask == 0) {
    327                 if (mBearerInitialVal == 0) {
    328                     bearers.add("" + 0);
    329                 }
    330             } else {
    331                 int i = 1;
    332                 while (bearerBitmask != 0) {
    333                     if ((bearerBitmask & 1) == 1) {
    334                         bearers.add("" + i);
    335                     }
    336                     bearerBitmask >>= 1;
    337                     i++;
    338                 }
    339             }
    340 
    341             if (mBearerInitialVal != 0 && bearers.contains("" + mBearerInitialVal) == false) {
    342                 // add mBearerInitialVal to bearers
    343                 bearers.add("" + mBearerInitialVal);
    344             }
    345             mBearerMulti.setValues(bearers);
    346 
    347             mMvnoType.setValue(mCursor.getString(MVNO_TYPE_INDEX));
    348             mMvnoMatchData.setEnabled(false);
    349             mMvnoMatchData.setText(mCursor.getString(MVNO_MATCH_DATA_INDEX));
    350             if (mNewApn && mMvnoTypeStr != null && mMvnoMatchDataStr != null) {
    351                 mMvnoType.setValue(mMvnoTypeStr);
    352                 mMvnoMatchData.setText(mMvnoMatchDataStr);
    353             }
    354         }
    355 
    356         mName.setSummary(checkNull(mName.getText()));
    357         mApn.setSummary(checkNull(mApn.getText()));
    358         mProxy.setSummary(checkNull(mProxy.getText()));
    359         mPort.setSummary(checkNull(mPort.getText()));
    360         mUser.setSummary(checkNull(mUser.getText()));
    361         mServer.setSummary(checkNull(mServer.getText()));
    362         mPassword.setSummary(starify(mPassword.getText()));
    363         mMmsProxy.setSummary(checkNull(mMmsProxy.getText()));
    364         mMmsPort.setSummary(checkNull(mMmsPort.getText()));
    365         mMmsc.setSummary(checkNull(mMmsc.getText()));
    366         mMcc.setSummary(checkNull(mMcc.getText()));
    367         mMnc.setSummary(checkNull(mMnc.getText()));
    368         mApnType.setSummary(checkNull(mApnType.getText()));
    369 
    370         String authVal = mAuthType.getValue();
    371         if (authVal != null) {
    372             int authValIndex = Integer.parseInt(authVal);
    373             mAuthType.setValueIndex(authValIndex);
    374 
    375             String []values = mRes.getStringArray(R.array.apn_auth_entries);
    376             mAuthType.setSummary(values[authValIndex]);
    377         } else {
    378             mAuthType.setSummary(sNotSet);
    379         }
    380 
    381         mProtocol.setSummary(
    382                 checkNull(protocolDescription(mProtocol.getValue(), mProtocol)));
    383         mRoamingProtocol.setSummary(
    384                 checkNull(protocolDescription(mRoamingProtocol.getValue(), mRoamingProtocol)));
    385         mBearerMulti.setSummary(
    386                 checkNull(bearerMultiDescription(mBearerMulti.getValues())));
    387         mMvnoType.setSummary(
    388                 checkNull(mvnoDescription(mMvnoType.getValue())));
    389         mMvnoMatchData.setSummary(checkNull(mMvnoMatchData.getText()));
    390         // allow user to edit carrier_enabled for some APN
    391         boolean ceEditable = getResources().getBoolean(R.bool.config_allow_edit_carrier_enabled);
    392         if (ceEditable) {
    393             mCarrierEnabled.setEnabled(true);
    394         } else {
    395             mCarrierEnabled.setEnabled(false);
    396         }
    397     }
    398 
    399     /**
    400      * Returns the UI choice (e.g., "IPv4/IPv6") corresponding to the given
    401      * raw value of the protocol preference (e.g., "IPV4V6"). If unknown,
    402      * return null.
    403      */
    404     private String protocolDescription(String raw, ListPreference protocol) {
    405         int protocolIndex = protocol.findIndexOfValue(raw);
    406         if (protocolIndex == -1) {
    407             return null;
    408         } else {
    409             String[] values = mRes.getStringArray(R.array.apn_protocol_entries);
    410             try {
    411                 return values[protocolIndex];
    412             } catch (ArrayIndexOutOfBoundsException e) {
    413                 return null;
    414             }
    415         }
    416     }
    417 
    418     private String bearerDescription(String raw) {
    419         int mBearerIndex = mBearerMulti.findIndexOfValue(raw);
    420         if (mBearerIndex == -1) {
    421             return null;
    422         } else {
    423             String[] values = mRes.getStringArray(R.array.bearer_entries);
    424             try {
    425                 return values[mBearerIndex];
    426             } catch (ArrayIndexOutOfBoundsException e) {
    427                 return null;
    428             }
    429         }
    430     }
    431 
    432     private String bearerMultiDescription(Set<String> raw) {
    433         String[] values = mRes.getStringArray(R.array.bearer_entries);
    434         StringBuilder retVal = new StringBuilder();
    435         boolean first = true;
    436         for (String bearer : raw) {
    437             int bearerIndex = mBearerMulti.findIndexOfValue(bearer);
    438             try {
    439                 if (first) {
    440                     retVal.append(values[bearerIndex]);
    441                     first = false;
    442                 } else {
    443                     retVal.append(", " + values[bearerIndex]);
    444                 }
    445             } catch (ArrayIndexOutOfBoundsException e) {
    446                 // ignore
    447             }
    448         }
    449         String val = retVal.toString();
    450         if (!TextUtils.isEmpty(val)) {
    451             return val;
    452         }
    453         return null;
    454     }
    455 
    456     private String mvnoDescription(String newValue) {
    457         int mvnoIndex = mMvnoType.findIndexOfValue(newValue);
    458         String oldValue = mMvnoType.getValue();
    459 
    460         if (mvnoIndex == -1) {
    461             return null;
    462         } else {
    463             String[] values = mRes.getStringArray(R.array.mvno_type_entries);
    464             mMvnoMatchData.setEnabled(mvnoIndex != 0);
    465             if (newValue != null && newValue.equals(oldValue) == false) {
    466                 if (values[mvnoIndex].equals("SPN")) {
    467                     mMvnoMatchData.setText(mTelephonyManager.getSimOperatorName());
    468                 } else if (values[mvnoIndex].equals("IMSI")) {
    469                     String numeric = mTelephonyManager.getSimOperator(mSubId);
    470                     mMvnoMatchData.setText(numeric + "x");
    471                 } else if (values[mvnoIndex].equals("GID")) {
    472                     mMvnoMatchData.setText(mTelephonyManager.getGroupIdLevel1());
    473                 }
    474             }
    475 
    476             try {
    477                 return values[mvnoIndex];
    478             } catch (ArrayIndexOutOfBoundsException e) {
    479                 return null;
    480             }
    481         }
    482     }
    483 
    484     public boolean onPreferenceChange(Preference preference, Object newValue) {
    485         String key = preference.getKey();
    486         if (KEY_AUTH_TYPE.equals(key)) {
    487             try {
    488                 int index = Integer.parseInt((String) newValue);
    489                 mAuthType.setValueIndex(index);
    490 
    491                 String[] values = mRes.getStringArray(R.array.apn_auth_entries);
    492                 mAuthType.setSummary(values[index]);
    493             } catch (NumberFormatException e) {
    494                 return false;
    495             }
    496         } else if (KEY_PROTOCOL.equals(key)) {
    497             String protocol = protocolDescription((String) newValue, mProtocol);
    498             if (protocol == null) {
    499                 return false;
    500             }
    501             mProtocol.setSummary(protocol);
    502             mProtocol.setValue((String) newValue);
    503         } else if (KEY_ROAMING_PROTOCOL.equals(key)) {
    504             String protocol = protocolDescription((String) newValue, mRoamingProtocol);
    505             if (protocol == null) {
    506                 return false;
    507             }
    508             mRoamingProtocol.setSummary(protocol);
    509             mRoamingProtocol.setValue((String) newValue);
    510         } else if (KEY_BEARER_MULTI.equals(key)) {
    511             String bearer = bearerMultiDescription((Set<String>) newValue);
    512             if (bearer == null) {
    513                 return false;
    514             }
    515             mBearerMulti.setValues((Set<String>) newValue);
    516             mBearerMulti.setSummary(bearer);
    517         } else if (KEY_MVNO_TYPE.equals(key)) {
    518             String mvno = mvnoDescription((String) newValue);
    519             if (mvno == null) {
    520                 return false;
    521             }
    522             mMvnoType.setValue((String) newValue);
    523             mMvnoType.setSummary(mvno);
    524         }
    525         if (preference.equals(mPassword)) {
    526             preference.setSummary(starify(newValue != null ? String.valueOf(newValue) : ""));
    527         } else if (preference.equals(mCarrierEnabled) || preference.equals(mBearerMulti)) {
    528             // do nothing
    529         } else {
    530             preference.setSummary(checkNull(newValue != null ? String.valueOf(newValue) : null));
    531         }
    532 
    533         return true;
    534     }
    535 
    536     @Override
    537     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    538         super.onCreateOptionsMenu(menu, inflater);
    539         // If it's a new APN, then cancel will delete the new entry in onPause
    540         if (!mNewApn) {
    541             menu.add(0, MENU_DELETE, 0, R.string.menu_delete)
    542                 .setIcon(R.drawable.ic_menu_delete);
    543         }
    544         menu.add(0, MENU_SAVE, 0, R.string.menu_save)
    545             .setIcon(android.R.drawable.ic_menu_save);
    546         menu.add(0, MENU_CANCEL, 0, R.string.menu_cancel)
    547             .setIcon(android.R.drawable.ic_menu_close_clear_cancel);
    548     }
    549 
    550     @Override
    551     public boolean onOptionsItemSelected(MenuItem item) {
    552         switch (item.getItemId()) {
    553         case MENU_DELETE:
    554             deleteApn();
    555             return true;
    556         case MENU_SAVE:
    557             if (validateAndSave(false)) {
    558                 finish();
    559             }
    560             return true;
    561         case MENU_CANCEL:
    562             if (mNewApn) {
    563                 getContentResolver().delete(mUri, null, null);
    564             }
    565             finish();
    566             return true;
    567         }
    568         return super.onOptionsItemSelected(item);
    569     }
    570 
    571     @Override
    572     public void onViewCreated(View view, Bundle savedInstanceState) {
    573         super.onViewCreated(view, savedInstanceState);
    574         view.setOnKeyListener(this);
    575     }
    576 
    577     public boolean onKey(View v, int keyCode, KeyEvent event) {
    578         if (event.getAction() != KeyEvent.ACTION_DOWN) return false;
    579         switch (keyCode) {
    580             case KeyEvent.KEYCODE_BACK: {
    581                 if (validateAndSave(false)) {
    582                     finish();
    583                 }
    584                 return true;
    585             }
    586         }
    587         return false;
    588     }
    589 
    590     @Override
    591     public void onSaveInstanceState(Bundle icicle) {
    592         super.onSaveInstanceState(icicle);
    593         if (validateAndSave(true)) {
    594             icicle.putInt(SAVED_POS, mCursor.getInt(ID_INDEX));
    595         }
    596     }
    597 
    598     /**
    599      * Check the key fields' validity and save if valid.
    600      * @param force save even if the fields are not valid, if the app is
    601      *        being suspended
    602      * @return true if the data was saved
    603      */
    604     private boolean validateAndSave(boolean force) {
    605         String name = checkNotSet(mName.getText());
    606         String apn = checkNotSet(mApn.getText());
    607         String mcc = checkNotSet(mMcc.getText());
    608         String mnc = checkNotSet(mMnc.getText());
    609 
    610         if (getErrorMsg() != null && !force) {
    611             ErrorDialog.showError(this);
    612             return false;
    613         }
    614 
    615         if (!mCursor.moveToFirst()) {
    616             Log.w(TAG,
    617                     "Could not go to the first row in the Cursor when saving data.");
    618             return false;
    619         }
    620 
    621         // If it's a new APN and a name or apn haven't been entered, then erase the entry
    622         if (force && mNewApn && name.length() < 1 && apn.length() < 1) {
    623             getContentResolver().delete(mUri, null, null);
    624             return false;
    625         }
    626 
    627         ContentValues values = new ContentValues();
    628 
    629         // Add a dummy name "Untitled", if the user exits the screen without adding a name but
    630         // entered other information worth keeping.
    631         values.put(Telephony.Carriers.NAME,
    632                 name.length() < 1 ? getResources().getString(R.string.untitled_apn) : name);
    633         values.put(Telephony.Carriers.APN, apn);
    634         values.put(Telephony.Carriers.PROXY, checkNotSet(mProxy.getText()));
    635         values.put(Telephony.Carriers.PORT, checkNotSet(mPort.getText()));
    636         values.put(Telephony.Carriers.MMSPROXY, checkNotSet(mMmsProxy.getText()));
    637         values.put(Telephony.Carriers.MMSPORT, checkNotSet(mMmsPort.getText()));
    638         values.put(Telephony.Carriers.USER, checkNotSet(mUser.getText()));
    639         values.put(Telephony.Carriers.SERVER, checkNotSet(mServer.getText()));
    640         values.put(Telephony.Carriers.PASSWORD, checkNotSet(mPassword.getText()));
    641         values.put(Telephony.Carriers.MMSC, checkNotSet(mMmsc.getText()));
    642 
    643         String authVal = mAuthType.getValue();
    644         if (authVal != null) {
    645             values.put(Telephony.Carriers.AUTH_TYPE, Integer.parseInt(authVal));
    646         }
    647 
    648         values.put(Telephony.Carriers.PROTOCOL, checkNotSet(mProtocol.getValue()));
    649         values.put(Telephony.Carriers.ROAMING_PROTOCOL, checkNotSet(mRoamingProtocol.getValue()));
    650 
    651         values.put(Telephony.Carriers.TYPE, checkNotSet(mApnType.getText()));
    652 
    653         values.put(Telephony.Carriers.MCC, mcc);
    654         values.put(Telephony.Carriers.MNC, mnc);
    655 
    656         values.put(Telephony.Carriers.NUMERIC, mcc + mnc);
    657 
    658         if (mCurMnc != null && mCurMcc != null) {
    659             if (mCurMnc.equals(mnc) && mCurMcc.equals(mcc)) {
    660                 values.put(Telephony.Carriers.CURRENT, 1);
    661             }
    662         }
    663 
    664         Set<String> bearerSet = mBearerMulti.getValues();
    665         int bearerBitmask = 0;
    666         for (String bearer : bearerSet) {
    667             if (Integer.parseInt(bearer) == 0) {
    668                 bearerBitmask = 0;
    669                 break;
    670             } else {
    671                 bearerBitmask |= ServiceState.getBitmaskForTech(Integer.parseInt(bearer));
    672             }
    673         }
    674         values.put(Telephony.Carriers.BEARER_BITMASK, bearerBitmask);
    675 
    676         int bearerVal;
    677         if (bearerBitmask == 0 || mBearerInitialVal == 0) {
    678             bearerVal = 0;
    679         } else if (ServiceState.bitmaskHasTech(bearerBitmask, mBearerInitialVal)) {
    680             bearerVal = mBearerInitialVal;
    681         } else {
    682             // bearer field was being used but bitmask has changed now and does not include the
    683             // initial bearer value -- setting bearer to 0 but maybe better behavior is to choose a
    684             // random tech from the new bitmask??
    685             bearerVal = 0;
    686         }
    687         values.put(Telephony.Carriers.BEARER, bearerVal);
    688 
    689         values.put(Telephony.Carriers.MVNO_TYPE, checkNotSet(mMvnoType.getValue()));
    690         values.put(Telephony.Carriers.MVNO_MATCH_DATA, checkNotSet(mMvnoMatchData.getText()));
    691 
    692         values.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled.isChecked() ? 1 : 0);
    693         getContentResolver().update(mUri, values, null, null);
    694 
    695         return true;
    696     }
    697 
    698     private String getErrorMsg() {
    699         String errorMsg = null;
    700 
    701         String name = checkNotSet(mName.getText());
    702         String apn = checkNotSet(mApn.getText());
    703         String mcc = checkNotSet(mMcc.getText());
    704         String mnc = checkNotSet(mMnc.getText());
    705 
    706         if (name.length() < 1) {
    707             errorMsg = mRes.getString(R.string.error_name_empty);
    708         } else if (apn.length() < 1) {
    709             errorMsg = mRes.getString(R.string.error_apn_empty);
    710         } else if (mcc.length() != 3) {
    711             errorMsg = mRes.getString(R.string.error_mcc_not3);
    712         } else if ((mnc.length() & 0xFFFE) != 2) {
    713             errorMsg = mRes.getString(R.string.error_mnc_not23);
    714         }
    715 
    716         return errorMsg;
    717     }
    718 
    719     private void deleteApn() {
    720         getContentResolver().delete(mUri, null, null);
    721         finish();
    722     }
    723 
    724     private String starify(String value) {
    725         if (value == null || value.length() == 0) {
    726             return sNotSet;
    727         } else {
    728             char[] password = new char[value.length()];
    729             for (int i = 0; i < password.length; i++) {
    730                 password[i] = '*';
    731             }
    732             return new String(password);
    733         }
    734     }
    735 
    736     private String checkNull(String value) {
    737         if (value == null || value.length() == 0) {
    738             return sNotSet;
    739         } else {
    740             return value;
    741         }
    742     }
    743 
    744     private String checkNotSet(String value) {
    745         if (value == null || value.equals(sNotSet)) {
    746             return "";
    747         } else {
    748             return value;
    749         }
    750     }
    751 
    752     public static class ErrorDialog extends DialogFragment {
    753 
    754         public static void showError(ApnEditor editor) {
    755             ErrorDialog dialog = new ErrorDialog();
    756             dialog.setTargetFragment(editor, 0);
    757             dialog.show(editor.getFragmentManager(), "error");
    758         }
    759 
    760         @Override
    761         public Dialog onCreateDialog(Bundle savedInstanceState) {
    762             String msg = ((ApnEditor) getTargetFragment()).getErrorMsg();
    763 
    764             return new AlertDialog.Builder(getContext())
    765                     .setTitle(R.string.error_title)
    766                     .setPositiveButton(android.R.string.ok, null)
    767                     .setMessage(msg)
    768                     .create();
    769         }
    770     }
    771 
    772 }
    773