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