OSDN Git Service

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