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