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