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