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.content.ContentUris;
     21 import android.content.ContentValues;
     22 import android.content.Intent;
     23 import android.content.SharedPreferences;
     24 import android.content.res.Resources;
     25 import android.database.Cursor;
     26 import android.net.Uri;
     27 import android.os.Bundle;
     28 import android.os.SystemProperties;
     29 import android.preference.EditTextPreference;
     30 import android.preference.ListPreference;
     31 import android.preference.Preference;
     32 import android.preference.PreferenceActivity;
     33 import android.provider.Telephony;
     34 import android.util.Log;
     35 import android.view.KeyEvent;
     36 import android.view.Menu;
     37 import android.view.MenuItem;
     38 
     39 import com.android.internal.telephony.TelephonyProperties;
     40 import com.android.internal.telephony.RILConstants;
     41 
     42 
     43 public class ApnEditor extends PreferenceActivity
     44         implements SharedPreferences.OnSharedPreferenceChangeListener,
     45                     Preference.OnPreferenceChangeListener {
     46 
     47     private final static String TAG = ApnEditor.class.getSimpleName();
     48 
     49     private final static String SAVED_POS = "pos";
     50     private final static String KEY_AUTH_TYPE = "auth_type";
     51     private final static String KEY_PROTOCOL = "apn_protocol";
     52 
     53     private static final int MENU_DELETE = Menu.FIRST;
     54     private static final int MENU_SAVE = Menu.FIRST + 1;
     55     private static final int MENU_CANCEL = Menu.FIRST + 2;
     56 
     57     private static String sNotSet;
     58     private EditTextPreference mName;
     59     private EditTextPreference mApn;
     60     private EditTextPreference mProxy;
     61     private EditTextPreference mPort;
     62     private EditTextPreference mUser;
     63     private EditTextPreference mServer;
     64     private EditTextPreference mPassword;
     65     private EditTextPreference mMmsc;
     66     private EditTextPreference mMcc;
     67     private EditTextPreference mMnc;
     68     private EditTextPreference mMmsProxy;
     69     private EditTextPreference mMmsPort;
     70     private ListPreference mAuthType;
     71     private EditTextPreference mApnType;
     72     private ListPreference mProtocol;
     73 
     74     private String mCurMnc;
     75     private String mCurMcc;
     76 
     77     private Uri mUri;
     78     private Cursor mCursor;
     79     private boolean mNewApn;
     80     private boolean mFirstTime;
     81     private Resources mRes;
     82 
     83     /**
     84      * Standard projection for the interesting columns of a normal note.
     85      */
     86     private static final String[] sProjection = new String[] {
     87             Telephony.Carriers._ID,     // 0
     88             Telephony.Carriers.NAME,    // 1
     89             Telephony.Carriers.APN,     // 2
     90             Telephony.Carriers.PROXY,   // 3
     91             Telephony.Carriers.PORT,    // 4
     92             Telephony.Carriers.USER,    // 5
     93             Telephony.Carriers.SERVER,  // 6
     94             Telephony.Carriers.PASSWORD, // 7
     95             Telephony.Carriers.MMSC, // 8
     96             Telephony.Carriers.MCC, // 9
     97             Telephony.Carriers.MNC, // 10
     98             Telephony.Carriers.NUMERIC, // 11
     99             Telephony.Carriers.MMSPROXY,// 12
    100             Telephony.Carriers.MMSPORT, // 13
    101             Telephony.Carriers.AUTH_TYPE, // 14
    102             Telephony.Carriers.TYPE, // 15
    103             Telephony.Carriers.PROTOCOL, // 16
    104     };
    105 
    106     private static final int ID_INDEX = 0;
    107     private static final int NAME_INDEX = 1;
    108     private static final int APN_INDEX = 2;
    109     private static final int PROXY_INDEX = 3;
    110     private static final int PORT_INDEX = 4;
    111     private static final int USER_INDEX = 5;
    112     private static final int SERVER_INDEX = 6;
    113     private static final int PASSWORD_INDEX = 7;
    114     private static final int MMSC_INDEX = 8;
    115     private static final int MCC_INDEX = 9;
    116     private static final int MNC_INDEX = 10;
    117     private static final int MMSPROXY_INDEX = 12;
    118     private static final int MMSPORT_INDEX = 13;
    119     private static final int AUTH_TYPE_INDEX = 14;
    120     private static final int TYPE_INDEX = 15;
    121     private static final int PROTOCOL_INDEX = 16;
    122 
    123 
    124     @Override
    125     protected void onCreate(Bundle icicle) {
    126         super.onCreate(icicle);
    127 
    128         addPreferencesFromResource(R.xml.apn_editor);
    129 
    130         sNotSet = getResources().getString(R.string.apn_not_set);
    131         mName = (EditTextPreference) findPreference("apn_name");
    132         mApn = (EditTextPreference) findPreference("apn_apn");
    133         mProxy = (EditTextPreference) findPreference("apn_http_proxy");
    134         mPort = (EditTextPreference) findPreference("apn_http_port");
    135         mUser = (EditTextPreference) findPreference("apn_user");
    136         mServer = (EditTextPreference) findPreference("apn_server");
    137         mPassword = (EditTextPreference) findPreference("apn_password");
    138         mMmsProxy = (EditTextPreference) findPreference("apn_mms_proxy");
    139         mMmsPort = (EditTextPreference) findPreference("apn_mms_port");
    140         mMmsc = (EditTextPreference) findPreference("apn_mmsc");
    141         mMcc = (EditTextPreference) findPreference("apn_mcc");
    142         mMnc = (EditTextPreference) findPreference("apn_mnc");
    143         mApnType = (EditTextPreference) findPreference("apn_type");
    144 
    145         mAuthType = (ListPreference) findPreference(KEY_AUTH_TYPE);
    146         mAuthType.setOnPreferenceChangeListener(this);
    147 
    148         mProtocol = (ListPreference) findPreference(KEY_PROTOCOL);
    149         mProtocol.setOnPreferenceChangeListener(this);
    150 
    151         mRes = getResources();
    152 
    153         final Intent intent = getIntent();
    154         final String action = intent.getAction();
    155 
    156         mFirstTime = icicle == null;
    157 
    158         if (action.equals(Intent.ACTION_EDIT)) {
    159             mUri = intent.getData();
    160         } else if (action.equals(Intent.ACTION_INSERT)) {
    161             if (mFirstTime || icicle.getInt(SAVED_POS) == 0) {
    162                 mUri = getContentResolver().insert(intent.getData(), new ContentValues());
    163             } else {
    164                 mUri = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI,
    165                         icicle.getInt(SAVED_POS));
    166             }
    167             mNewApn = true;
    168             // If we were unable to create a new note, then just finish
    169             // this activity.  A RESULT_CANCELED will be sent back to the
    170             // original activity if they requested a result.
    171             if (mUri == null) {
    172                 Log.w(TAG, "Failed to insert new telephony provider into "
    173                         + getIntent().getData());
    174                 finish();
    175                 return;
    176             }
    177 
    178             // The new entry was created, so assume all will end well and
    179             // set the result to be returned.
    180             setResult(RESULT_OK, (new Intent()).setAction(mUri.toString()));
    181 
    182         } else {
    183             finish();
    184             return;
    185         }
    186 
    187         mCursor = managedQuery(mUri, sProjection, null, null);
    188         mCursor.moveToFirst();
    189 
    190         fillUi();
    191     }
    192 
    193     @Override
    194     public void onResume() {
    195         super.onResume();
    196         getPreferenceScreen().getSharedPreferences()
    197                 .registerOnSharedPreferenceChangeListener(this);
    198     }
    199 
    200     @Override
    201     public void onPause() {
    202         getPreferenceScreen().getSharedPreferences()
    203                 .unregisterOnSharedPreferenceChangeListener(this);
    204         super.onPause();
    205     }
    206 
    207     private void fillUi() {
    208         if (mFirstTime) {
    209             mFirstTime = false;
    210             // Fill in all the values from the db in both text editor and summary
    211             mName.setText(mCursor.getString(NAME_INDEX));
    212             mApn.setText(mCursor.getString(APN_INDEX));
    213             mProxy.setText(mCursor.getString(PROXY_INDEX));
    214             mPort.setText(mCursor.getString(PORT_INDEX));
    215             mUser.setText(mCursor.getString(USER_INDEX));
    216             mServer.setText(mCursor.getString(SERVER_INDEX));
    217             mPassword.setText(mCursor.getString(PASSWORD_INDEX));
    218             mMmsProxy.setText(mCursor.getString(MMSPROXY_INDEX));
    219             mMmsPort.setText(mCursor.getString(MMSPORT_INDEX));
    220             mMmsc.setText(mCursor.getString(MMSC_INDEX));
    221             mMcc.setText(mCursor.getString(MCC_INDEX));
    222             mMnc.setText(mCursor.getString(MNC_INDEX));
    223             mApnType.setText(mCursor.getString(TYPE_INDEX));
    224             if (mNewApn) {
    225                 String numeric =
    226                     SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC);
    227                 // MCC is first 3 chars and then in 2 - 3 chars of MNC
    228                 if (numeric != null && numeric.length() > 4) {
    229                     // Country code
    230                     String mcc = numeric.substring(0, 3);
    231                     // Network code
    232                     String mnc = numeric.substring(3);
    233                     // Auto populate MNC and MCC for new entries, based on what SIM reports
    234                     mMcc.setText(mcc);
    235                     mMnc.setText(mnc);
    236                     mCurMnc = mnc;
    237                     mCurMcc = mcc;
    238                 }
    239             }
    240             int authVal = mCursor.getInt(AUTH_TYPE_INDEX);
    241             if (authVal != -1) {
    242                 mAuthType.setValueIndex(authVal);
    243             }
    244 
    245             mProtocol.setValue(mCursor.getString(PROTOCOL_INDEX));
    246         }
    247 
    248         mName.setSummary(checkNull(mName.getText()));
    249         mApn.setSummary(checkNull(mApn.getText()));
    250         mProxy.setSummary(checkNull(mProxy.getText()));
    251         mPort.setSummary(checkNull(mPort.getText()));
    252         mUser.setSummary(checkNull(mUser.getText()));
    253         mServer.setSummary(checkNull(mServer.getText()));
    254         mPassword.setSummary(starify(mPassword.getText()));
    255         mMmsProxy.setSummary(checkNull(mMmsProxy.getText()));
    256         mMmsPort.setSummary(checkNull(mMmsPort.getText()));
    257         mMmsc.setSummary(checkNull(mMmsc.getText()));
    258         mMcc.setSummary(checkNull(mMcc.getText()));
    259         mMnc.setSummary(checkNull(mMnc.getText()));
    260         mApnType.setSummary(checkNull(mApnType.getText()));
    261 
    262         String authVal = mAuthType.getValue();
    263         if (authVal != null) {
    264             int authValIndex = Integer.parseInt(authVal);
    265             mAuthType.setValueIndex(authValIndex);
    266 
    267             String []values = mRes.getStringArray(R.array.apn_auth_entries);
    268             mAuthType.setSummary(values[authValIndex]);
    269         } else {
    270             mAuthType.setSummary(sNotSet);
    271         }
    272 
    273         mProtocol.setSummary(
    274                 checkNull(protocolDescription(mProtocol.getValue())));
    275     }
    276 
    277     /**
    278      * Returns the UI choice (e.g., "IPv4/IPv6") corresponding to the given
    279      * raw value of the protocol preference (e.g., "IPV4V6"). If unknown,
    280      * return null.
    281      */
    282     private String protocolDescription(String raw) {
    283         int protocolIndex = mProtocol.findIndexOfValue(raw);
    284         if (protocolIndex == -1) {
    285             return null;
    286         } else {
    287             String[] values = mRes.getStringArray(R.array.apn_protocol_entries);
    288             try {
    289                 return values[protocolIndex];
    290             } catch (ArrayIndexOutOfBoundsException e) {
    291                 return null;
    292             }
    293         }
    294     }
    295 
    296     public boolean onPreferenceChange(Preference preference, Object newValue) {
    297         String key = preference.getKey();
    298         if (KEY_AUTH_TYPE.equals(key)) {
    299             try {
    300                 int index = Integer.parseInt((String) newValue);
    301                 mAuthType.setValueIndex(index);
    302 
    303                 String []values = mRes.getStringArray(R.array.apn_auth_entries);
    304                 mAuthType.setSummary(values[index]);
    305             } catch (NumberFormatException e) {
    306                 return false;
    307             }
    308             return true;
    309         }
    310 
    311         if (KEY_PROTOCOL.equals(key)) {
    312             String protocol = protocolDescription((String) newValue);
    313             if (protocol == null) {
    314                 return false;
    315             }
    316             mProtocol.setSummary(protocol);
    317             mProtocol.setValue((String) newValue);
    318         }
    319         return true;
    320     }
    321 
    322     @Override
    323     public boolean onCreateOptionsMenu(Menu menu) {
    324         super.onCreateOptionsMenu(menu);
    325         // If it's a new APN, then cancel will delete the new entry in onPause
    326         if (!mNewApn) {
    327             menu.add(0, MENU_DELETE, 0, R.string.menu_delete)
    328                 .setIcon(android.R.drawable.ic_menu_delete);
    329         }
    330         menu.add(0, MENU_SAVE, 0, R.string.menu_save)
    331             .setIcon(android.R.drawable.ic_menu_save);
    332         menu.add(0, MENU_CANCEL, 0, R.string.menu_cancel)
    333             .setIcon(android.R.drawable.ic_menu_close_clear_cancel);
    334         return true;
    335     }
    336 
    337     @Override
    338     public boolean onOptionsItemSelected(MenuItem item) {
    339         switch (item.getItemId()) {
    340         case MENU_DELETE:
    341             deleteApn();
    342             return true;
    343         case MENU_SAVE:
    344             if (validateAndSave(false)) {
    345                 finish();
    346             }
    347             return true;
    348         case MENU_CANCEL:
    349             if (mNewApn) {
    350                 getContentResolver().delete(mUri, null, null);
    351             }
    352             finish();
    353             return true;
    354         }
    355         return super.onOptionsItemSelected(item);
    356     }
    357 
    358     @Override
    359     public boolean onKeyDown(int keyCode, KeyEvent event) {
    360         switch (keyCode) {
    361             case KeyEvent.KEYCODE_BACK: {
    362                 if (validateAndSave(false)) {
    363                     finish();
    364                 }
    365                 return true;
    366             }
    367         }
    368         return super.onKeyDown(keyCode, event);
    369     }
    370 
    371     @Override
    372     protected void onSaveInstanceState(Bundle icicle) {
    373         super.onSaveInstanceState(icicle);
    374         if (validateAndSave(true)) {
    375             icicle.putInt(SAVED_POS, mCursor.getInt(ID_INDEX));
    376         }
    377     }
    378 
    379     /**
    380      * Check the key fields' validity and save if valid.
    381      * @param force save even if the fields are not valid, if the app is
    382      *        being suspended
    383      * @return true if the data was saved
    384      */
    385     private boolean validateAndSave(boolean force) {
    386         String name = checkNotSet(mName.getText());
    387         String apn = checkNotSet(mApn.getText());
    388         String mcc = checkNotSet(mMcc.getText());
    389         String mnc = checkNotSet(mMnc.getText());
    390 
    391         String errorMsg = null;
    392         if (name.length() < 1) {
    393             errorMsg = mRes.getString(R.string.error_name_empty);
    394         } else if (apn.length() < 1) {
    395             errorMsg = mRes.getString(R.string.error_apn_empty);
    396         } else if (mcc.length() != 3) {
    397             errorMsg = mRes.getString(R.string.error_mcc_not3);
    398         } else if ((mnc.length() & 0xFFFE) != 2) {
    399             errorMsg = mRes.getString(R.string.error_mnc_not23);
    400         }
    401 
    402         if (errorMsg != null && !force) {
    403             showErrorMessage(errorMsg);
    404             return false;
    405         }
    406 
    407         if (!mCursor.moveToFirst()) {
    408             Log.w(TAG,
    409                     "Could not go to the first row in the Cursor when saving data.");
    410             return false;
    411         }
    412 
    413         // If it's a new APN and a name or apn haven't been entered, then erase the entry
    414         if (force && mNewApn && name.length() < 1 && apn.length() < 1) {
    415             getContentResolver().delete(mUri, null, null);
    416             return false;
    417         }
    418 
    419         ContentValues values = new ContentValues();
    420 
    421         // Add a dummy name "Untitled", if the user exits the screen without adding a name but
    422         // entered other information worth keeping.
    423         values.put(Telephony.Carriers.NAME,
    424                 name.length() < 1 ? getResources().getString(R.string.untitled_apn) : name);
    425         values.put(Telephony.Carriers.APN, apn);
    426         values.put(Telephony.Carriers.PROXY, checkNotSet(mProxy.getText()));
    427         values.put(Telephony.Carriers.PORT, checkNotSet(mPort.getText()));
    428         values.put(Telephony.Carriers.MMSPROXY, checkNotSet(mMmsProxy.getText()));
    429         values.put(Telephony.Carriers.MMSPORT, checkNotSet(mMmsPort.getText()));
    430         values.put(Telephony.Carriers.USER, checkNotSet(mUser.getText()));
    431         values.put(Telephony.Carriers.SERVER, checkNotSet(mServer.getText()));
    432         values.put(Telephony.Carriers.PASSWORD, checkNotSet(mPassword.getText()));
    433         values.put(Telephony.Carriers.MMSC, checkNotSet(mMmsc.getText()));
    434 
    435         String authVal = mAuthType.getValue();
    436         if (authVal != null) {
    437             values.put(Telephony.Carriers.AUTH_TYPE, Integer.parseInt(authVal));
    438         }
    439 
    440         values.put(Telephony.Carriers.PROTOCOL, checkNotSet(mProtocol.getValue()));
    441 
    442         // Hardcode IPv4 roaming for now until the carriers sort out all the
    443         // billing arrangements.
    444         values.put(Telephony.Carriers.ROAMING_PROTOCOL,
    445                 RILConstants.SETUP_DATA_PROTOCOL_IP);
    446 
    447         values.put(Telephony.Carriers.TYPE, checkNotSet(mApnType.getText()));
    448 
    449         values.put(Telephony.Carriers.MCC, mcc);
    450         values.put(Telephony.Carriers.MNC, mnc);
    451 
    452         values.put(Telephony.Carriers.NUMERIC, mcc + mnc);
    453 
    454         if (mCurMnc != null && mCurMcc != null) {
    455             if (mCurMnc.equals(mnc) && mCurMcc.equals(mcc)) {
    456                 values.put(Telephony.Carriers.CURRENT, 1);
    457             }
    458         }
    459 
    460         getContentResolver().update(mUri, values, null, null);
    461 
    462         return true;
    463     }
    464 
    465     private void showErrorMessage(String message) {
    466         new AlertDialog.Builder(this)
    467             .setTitle(R.string.error_title)
    468             .setMessage(message)
    469             .setPositiveButton(android.R.string.ok, null)
    470             .show();
    471     }
    472 
    473     private void deleteApn() {
    474         getContentResolver().delete(mUri, null, null);
    475         finish();
    476     }
    477 
    478     private String starify(String value) {
    479         if (value == null || value.length() == 0) {
    480             return sNotSet;
    481         } else {
    482             char[] password = new char[value.length()];
    483             for (int i = 0; i < password.length; i++) {
    484                 password[i] = '*';
    485             }
    486             return new String(password);
    487         }
    488     }
    489 
    490     private String checkNull(String value) {
    491         if (value == null || value.length() == 0) {
    492             return sNotSet;
    493         } else {
    494             return value;
    495         }
    496     }
    497 
    498     private String checkNotSet(String value) {
    499         if (value == null || value.equals(sNotSet)) {
    500             return "";
    501         } else {
    502             return value;
    503         }
    504     }
    505 
    506     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
    507         Preference pref = findPreference(key);
    508         if (pref != null) {
    509             if (pref.equals(mPassword)){
    510                 pref.setSummary(starify(sharedPreferences.getString(key, "")));
    511             } else {
    512                 pref.setSummary(checkNull(sharedPreferences.getString(key, "")));
    513             }
    514         }
    515     }
    516 }
    517