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