1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.settings; 18 19import android.app.AlertDialog; 20import android.app.Dialog; 21import android.content.ContentUris; 22import android.content.ContentValues; 23import android.content.Context; 24import android.content.Intent; 25import android.content.res.Resources; 26import android.database.Cursor; 27import android.net.Uri; 28import android.os.Bundle; 29import android.os.PersistableBundle; 30import android.provider.Telephony; 31import android.support.v14.preference.MultiSelectListPreference; 32import android.support.v14.preference.SwitchPreference; 33import android.support.v7.preference.EditTextPreference; 34import android.support.v7.preference.ListPreference; 35import android.support.v7.preference.Preference; 36import android.support.v7.preference.Preference.OnPreferenceChangeListener; 37import android.telephony.CarrierConfigManager; 38import android.telephony.ServiceState; 39import android.telephony.SubscriptionManager; 40import android.telephony.TelephonyManager; 41import android.text.TextUtils; 42import android.util.Log; 43import android.view.KeyEvent; 44import android.view.Menu; 45import android.view.MenuInflater; 46import android.view.MenuItem; 47import android.view.View; 48import android.view.View.OnKeyListener; 49 50import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 51import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 52import com.android.internal.telephony.PhoneConstants; 53import com.android.internal.util.ArrayUtils; 54 55import java.util.ArrayList; 56import java.util.Arrays; 57import java.util.HashSet; 58import java.util.List; 59import java.util.Set; 60 61import static android.app.Activity.RESULT_OK; 62import static android.content.Context.TELEPHONY_SERVICE; 63 64public class ApnEditor extends SettingsPreferenceFragment 65 implements OnPreferenceChangeListener, OnKeyListener { 66 67 private final static String TAG = ApnEditor.class.getSimpleName(); 68 private final static boolean VDBG = false; // STOPSHIP if true 69 70 private final static String SAVED_POS = "pos"; 71 private final static String KEY_AUTH_TYPE = "auth_type"; 72 private final static String KEY_PROTOCOL = "apn_protocol"; 73 private final static String KEY_ROAMING_PROTOCOL = "apn_roaming_protocol"; 74 private final static String KEY_CARRIER_ENABLED = "carrier_enabled"; 75 private final static String KEY_BEARER_MULTI = "bearer_multi"; 76 private final static String KEY_MVNO_TYPE = "mvno_type"; 77 private final static String KEY_PASSWORD = "apn_password"; 78 79 private static final int MENU_DELETE = Menu.FIRST; 80 private static final int MENU_SAVE = Menu.FIRST + 1; 81 private static final int MENU_CANCEL = Menu.FIRST + 2; 82 83 private static String sNotSet; 84 private EditTextPreference mName; 85 private EditTextPreference mApn; 86 private EditTextPreference mProxy; 87 private EditTextPreference mPort; 88 private EditTextPreference mUser; 89 private EditTextPreference mServer; 90 private EditTextPreference mPassword; 91 private EditTextPreference mMmsc; 92 private EditTextPreference mMcc; 93 private EditTextPreference mMnc; 94 private EditTextPreference mMmsProxy; 95 private EditTextPreference mMmsPort; 96 private ListPreference mAuthType; 97 private EditTextPreference mApnType; 98 private ListPreference mProtocol; 99 private ListPreference mRoamingProtocol; 100 private SwitchPreference mCarrierEnabled; 101 private MultiSelectListPreference mBearerMulti; 102 private ListPreference mMvnoType; 103 private EditTextPreference mMvnoMatchData; 104 105 private String mCurMnc; 106 private String mCurMcc; 107 108 private Uri mUri; 109 private Cursor mCursor; 110 private boolean mNewApn; 111 private boolean mFirstTime; 112 private int mSubId; 113 private Resources mRes; 114 private TelephonyManager mTelephonyManager; 115 private int mBearerInitialVal = 0; 116 private String mMvnoTypeStr; 117 private String mMvnoMatchDataStr; 118 private String[] mReadOnlyApnTypes; 119 private String[] mReadOnlyApnFields; 120 private boolean mReadOnlyApn; 121 122 /** 123 * Standard projection for the interesting columns of a normal note. 124 */ 125 private static final String[] sProjection = new String[] { 126 Telephony.Carriers._ID, // 0 127 Telephony.Carriers.NAME, // 1 128 Telephony.Carriers.APN, // 2 129 Telephony.Carriers.PROXY, // 3 130 Telephony.Carriers.PORT, // 4 131 Telephony.Carriers.USER, // 5 132 Telephony.Carriers.SERVER, // 6 133 Telephony.Carriers.PASSWORD, // 7 134 Telephony.Carriers.MMSC, // 8 135 Telephony.Carriers.MCC, // 9 136 Telephony.Carriers.MNC, // 10 137 Telephony.Carriers.NUMERIC, // 11 138 Telephony.Carriers.MMSPROXY,// 12 139 Telephony.Carriers.MMSPORT, // 13 140 Telephony.Carriers.AUTH_TYPE, // 14 141 Telephony.Carriers.TYPE, // 15 142 Telephony.Carriers.PROTOCOL, // 16 143 Telephony.Carriers.CARRIER_ENABLED, // 17 144 Telephony.Carriers.BEARER, // 18 145 Telephony.Carriers.BEARER_BITMASK, // 19 146 Telephony.Carriers.ROAMING_PROTOCOL, // 20 147 Telephony.Carriers.MVNO_TYPE, // 21 148 Telephony.Carriers.MVNO_MATCH_DATA, // 22 149 Telephony.Carriers.EDITED, // 23 150 Telephony.Carriers.USER_EDITABLE //24 151 }; 152 153 private static final int ID_INDEX = 0; 154 private static final int NAME_INDEX = 1; 155 private static final int APN_INDEX = 2; 156 private static final int PROXY_INDEX = 3; 157 private static final int PORT_INDEX = 4; 158 private static final int USER_INDEX = 5; 159 private static final int SERVER_INDEX = 6; 160 private static final int PASSWORD_INDEX = 7; 161 private static final int MMSC_INDEX = 8; 162 private static final int MCC_INDEX = 9; 163 private static final int MNC_INDEX = 10; 164 private static final int MMSPROXY_INDEX = 12; 165 private static final int MMSPORT_INDEX = 13; 166 private static final int AUTH_TYPE_INDEX = 14; 167 private static final int TYPE_INDEX = 15; 168 private static final int PROTOCOL_INDEX = 16; 169 private static final int CARRIER_ENABLED_INDEX = 17; 170 private static final int BEARER_INDEX = 18; 171 private static final int BEARER_BITMASK_INDEX = 19; 172 private static final int ROAMING_PROTOCOL_INDEX = 20; 173 private static final int MVNO_TYPE_INDEX = 21; 174 private static final int MVNO_MATCH_DATA_INDEX = 22; 175 private static final int EDITED_INDEX = 23; 176 private static final int USER_EDITABLE_INDEX = 24; 177 178 179 @Override 180 public void onCreate(Bundle icicle) { 181 super.onCreate(icicle); 182 183 addPreferencesFromResource(R.xml.apn_editor); 184 185 sNotSet = getResources().getString(R.string.apn_not_set); 186 mName = (EditTextPreference) findPreference("apn_name"); 187 mApn = (EditTextPreference) findPreference("apn_apn"); 188 mProxy = (EditTextPreference) findPreference("apn_http_proxy"); 189 mPort = (EditTextPreference) findPreference("apn_http_port"); 190 mUser = (EditTextPreference) findPreference("apn_user"); 191 mServer = (EditTextPreference) findPreference("apn_server"); 192 mPassword = (EditTextPreference) findPreference(KEY_PASSWORD); 193 mMmsProxy = (EditTextPreference) findPreference("apn_mms_proxy"); 194 mMmsPort = (EditTextPreference) findPreference("apn_mms_port"); 195 mMmsc = (EditTextPreference) findPreference("apn_mmsc"); 196 mMcc = (EditTextPreference) findPreference("apn_mcc"); 197 mMnc = (EditTextPreference) findPreference("apn_mnc"); 198 mApnType = (EditTextPreference) findPreference("apn_type"); 199 mAuthType = (ListPreference) findPreference(KEY_AUTH_TYPE); 200 mProtocol = (ListPreference) findPreference(KEY_PROTOCOL); 201 mRoamingProtocol = (ListPreference) findPreference(KEY_ROAMING_PROTOCOL); 202 mCarrierEnabled = (SwitchPreference) findPreference(KEY_CARRIER_ENABLED); 203 mBearerMulti = (MultiSelectListPreference) findPreference(KEY_BEARER_MULTI); 204 mMvnoType = (ListPreference) findPreference(KEY_MVNO_TYPE); 205 mMvnoMatchData = (EditTextPreference) findPreference("mvno_match_data"); 206 207 mRes = getResources(); 208 209 final Intent intent = getIntent(); 210 final String action = intent.getAction(); 211 mSubId = intent.getIntExtra(ApnSettings.SUB_ID, 212 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 213 214 mFirstTime = icicle == null; 215 mReadOnlyApn = false; 216 mReadOnlyApnTypes = null; 217 mReadOnlyApnFields = null; 218 219 CarrierConfigManager configManager = (CarrierConfigManager) 220 getSystemService(Context.CARRIER_CONFIG_SERVICE); 221 if (configManager != null) { 222 PersistableBundle b = configManager.getConfig(); 223 if (b != null) { 224 mReadOnlyApnTypes = b.getStringArray( 225 CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY); 226 if (!ArrayUtils.isEmpty(mReadOnlyApnTypes)) { 227 for (String apnType : mReadOnlyApnTypes) { 228 Log.d(TAG, "onCreate: read only APN type: " + apnType); 229 } 230 } 231 mReadOnlyApnFields = b.getStringArray( 232 CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY); 233 } 234 } 235 236 if (action.equals(Intent.ACTION_EDIT)) { 237 Uri uri = intent.getData(); 238 if (!uri.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) { 239 Log.e(TAG, "Edit request not for carrier table. Uri: " + uri); 240 finish(); 241 return; 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 (mCursor.getInt(USER_EDITABLE_INDEX) == 0 || 288 apnTypesMatch(mReadOnlyApnTypes, mCursor.getString(TYPE_INDEX)))) { 289 Log.d(TAG, "onCreate: apnTypesMatch; read-only APN"); 290 mReadOnlyApn = true; 291 disableAllFields(); 292 } else if (!ArrayUtils.isEmpty(mReadOnlyApnFields)) { 293 disableFields(mReadOnlyApnFields); 294 } 295 296 for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) { 297 getPreferenceScreen().getPreference(i).setOnPreferenceChangeListener(this); 298 } 299 300 } 301 302 @Override 303 public void onActivityCreated(Bundle savedInstanceState) { 304 super.onActivityCreated(savedInstanceState); 305 fillUi(); 306 } 307 308 /** 309 * Check if passed in array of APN types indicates all APN types 310 * @param apnTypes array of APN types. "*" indicates all types. 311 * @return true if all apn types are included in the array, false otherwise 312 */ 313 static boolean hasAllApns(String[] apnTypes) { 314 if (ArrayUtils.isEmpty(apnTypes)) { 315 return false; 316 } 317 318 List apnList = Arrays.asList(apnTypes); 319 if (apnList.contains(PhoneConstants.APN_TYPE_ALL)) { 320 Log.d(TAG, "hasAllApns: true because apnList.contains(PhoneConstants.APN_TYPE_ALL)"); 321 return true; 322 } 323 for (String apn : PhoneConstants.APN_TYPES) { 324 if (!apnList.contains(apn)) { 325 return false; 326 } 327 } 328 329 Log.d(TAG, "hasAllApns: true"); 330 return true; 331 } 332 333 /** 334 * Check if APN types overlap. 335 * @param apnTypesArray1 array of APNs. Empty array indicates no APN type; "*" indicates all 336 * types 337 * @param apnTypes2 comma separated string of APN types. Empty string represents all types. 338 * @return if any apn type matches return true, otherwise return false 339 */ 340 private boolean apnTypesMatch(String[] apnTypesArray1, String apnTypes2) { 341 if (ArrayUtils.isEmpty(apnTypesArray1)) { 342 return false; 343 } 344 345 if (hasAllApns(apnTypesArray1) || TextUtils.isEmpty(apnTypes2)) { 346 return true; 347 } 348 349 List apnTypesList1 = Arrays.asList(apnTypesArray1); 350 String[] apnTypesArray2 = apnTypes2.split(","); 351 352 for (String apn : apnTypesArray2) { 353 if (apnTypesList1.contains(apn.trim())) { 354 Log.d(TAG, "apnTypesMatch: true because match found for " + apn.trim()); 355 return true; 356 } 357 } 358 359 Log.d(TAG, "apnTypesMatch: false"); 360 return false; 361 } 362 363 /** 364 * Function to get Preference obj corresponding to an apnField 365 * @param apnField apn field name for which pref is needed 366 * @return Preference obj corresponding to passed in apnField 367 */ 368 private Preference getPreferenceFromFieldName(String apnField) { 369 switch (apnField) { 370 case Telephony.Carriers.NAME: 371 return mName; 372 case Telephony.Carriers.APN: 373 return mApn; 374 case Telephony.Carriers.PROXY: 375 return mProxy; 376 case Telephony.Carriers.PORT: 377 return mPort; 378 case Telephony.Carriers.USER: 379 return mUser; 380 case Telephony.Carriers.SERVER: 381 return mServer; 382 case Telephony.Carriers.PASSWORD: 383 return mPassword; 384 case Telephony.Carriers.MMSPROXY: 385 return mMmsProxy; 386 case Telephony.Carriers.MMSPORT: 387 return mMmsPort; 388 case Telephony.Carriers.MMSC: 389 return mMmsc; 390 case Telephony.Carriers.MCC: 391 return mMcc; 392 case Telephony.Carriers.MNC: 393 return mMnc; 394 case Telephony.Carriers.TYPE: 395 return mApnType; 396 case Telephony.Carriers.AUTH_TYPE: 397 return mAuthType; 398 case Telephony.Carriers.PROTOCOL: 399 return mProtocol; 400 case Telephony.Carriers.ROAMING_PROTOCOL: 401 return mRoamingProtocol; 402 case Telephony.Carriers.CARRIER_ENABLED: 403 return mCarrierEnabled; 404 case Telephony.Carriers.BEARER: 405 case Telephony.Carriers.BEARER_BITMASK: 406 return mBearerMulti; 407 case Telephony.Carriers.MVNO_TYPE: 408 return mMvnoType; 409 case Telephony.Carriers.MVNO_MATCH_DATA: 410 return mMvnoMatchData; 411 } 412 return null; 413 } 414 415 /** 416 * Disables given fields so that user cannot modify them 417 * 418 * @param apnFields fields to be disabled 419 */ 420 private void disableFields(String[] apnFields) { 421 for (String apnField : apnFields) { 422 Preference preference = getPreferenceFromFieldName(apnField); 423 if (preference != null) { 424 preference.setEnabled(false); 425 } 426 } 427 } 428 429 /** 430 * Disables all fields so that user cannot modify the APN 431 */ 432 private void disableAllFields() { 433 mName.setEnabled(false); 434 mApn.setEnabled(false); 435 mProxy.setEnabled(false); 436 mPort.setEnabled(false); 437 mUser.setEnabled(false); 438 mServer.setEnabled(false); 439 mPassword.setEnabled(false); 440 mMmsProxy.setEnabled(false); 441 mMmsPort.setEnabled(false); 442 mMmsc.setEnabled(false); 443 mMcc.setEnabled(false); 444 mMnc.setEnabled(false); 445 mApnType.setEnabled(false); 446 mAuthType.setEnabled(false); 447 mProtocol.setEnabled(false); 448 mRoamingProtocol.setEnabled(false); 449 mCarrierEnabled.setEnabled(false); 450 mBearerMulti.setEnabled(false); 451 mMvnoType.setEnabled(false); 452 mMvnoMatchData.setEnabled(false); 453 } 454 455 @Override 456 public int getMetricsCategory() { 457 return MetricsEvent.APN_EDITOR; 458 } 459 460 @Override 461 public void onResume() { 462 super.onResume(); 463 464 if (mUri == null && mNewApn) { 465 // The URI could have been deleted when activity is paused, 466 // therefore, it needs to be restored. 467 mUri = getContentResolver().insert(getIntent().getData(), new ContentValues()); 468 if (mUri == null) { 469 Log.w(TAG, "Failed to insert new telephony provider into " 470 + getIntent().getData()); 471 finish(); 472 return; 473 } 474 mCursor = getActivity().managedQuery(mUri, sProjection, null, null); 475 mCursor.moveToFirst(); 476 } 477 478 } 479 480 @Override 481 public void onPause() { 482 super.onPause(); 483 } 484 485 private void fillUi() { 486 if (mFirstTime) { 487 mFirstTime = false; 488 // Fill in all the values from the db in both text editor and summary 489 mName.setText(mCursor.getString(NAME_INDEX)); 490 mApn.setText(mCursor.getString(APN_INDEX)); 491 mProxy.setText(mCursor.getString(PROXY_INDEX)); 492 mPort.setText(mCursor.getString(PORT_INDEX)); 493 mUser.setText(mCursor.getString(USER_INDEX)); 494 mServer.setText(mCursor.getString(SERVER_INDEX)); 495 mPassword.setText(mCursor.getString(PASSWORD_INDEX)); 496 mMmsProxy.setText(mCursor.getString(MMSPROXY_INDEX)); 497 mMmsPort.setText(mCursor.getString(MMSPORT_INDEX)); 498 mMmsc.setText(mCursor.getString(MMSC_INDEX)); 499 mMcc.setText(mCursor.getString(MCC_INDEX)); 500 mMnc.setText(mCursor.getString(MNC_INDEX)); 501 mApnType.setText(mCursor.getString(TYPE_INDEX)); 502 if (mNewApn) { 503 String numeric = mTelephonyManager.getSimOperator(mSubId); 504 // MCC is first 3 chars and then in 2 - 3 chars of MNC 505 if (numeric != null && numeric.length() > 4) { 506 // Country code 507 String mcc = numeric.substring(0, 3); 508 // Network code 509 String mnc = numeric.substring(3); 510 // Auto populate MNC and MCC for new entries, based on what SIM reports 511 mMcc.setText(mcc); 512 mMnc.setText(mnc); 513 mCurMnc = mnc; 514 mCurMcc = mcc; 515 } 516 } 517 int authVal = mCursor.getInt(AUTH_TYPE_INDEX); 518 if (authVal != -1) { 519 mAuthType.setValueIndex(authVal); 520 } else { 521 mAuthType.setValue(null); 522 } 523 524 mProtocol.setValue(mCursor.getString(PROTOCOL_INDEX)); 525 mRoamingProtocol.setValue(mCursor.getString(ROAMING_PROTOCOL_INDEX)); 526 mCarrierEnabled.setChecked(mCursor.getInt(CARRIER_ENABLED_INDEX)==1); 527 mBearerInitialVal = mCursor.getInt(BEARER_INDEX); 528 529 HashSet<String> bearers = new HashSet<String>(); 530 int bearerBitmask = mCursor.getInt(BEARER_BITMASK_INDEX); 531 if (bearerBitmask == 0) { 532 if (mBearerInitialVal == 0) { 533 bearers.add("" + 0); 534 } 535 } else { 536 int i = 1; 537 while (bearerBitmask != 0) { 538 if ((bearerBitmask & 1) == 1) { 539 bearers.add("" + i); 540 } 541 bearerBitmask >>= 1; 542 i++; 543 } 544 } 545 546 if (mBearerInitialVal != 0 && bearers.contains("" + mBearerInitialVal) == false) { 547 // add mBearerInitialVal to bearers 548 bearers.add("" + mBearerInitialVal); 549 } 550 mBearerMulti.setValues(bearers); 551 552 mMvnoType.setValue(mCursor.getString(MVNO_TYPE_INDEX)); 553 mMvnoMatchData.setEnabled(false); 554 mMvnoMatchData.setText(mCursor.getString(MVNO_MATCH_DATA_INDEX)); 555 if (mNewApn && mMvnoTypeStr != null && mMvnoMatchDataStr != null) { 556 mMvnoType.setValue(mMvnoTypeStr); 557 mMvnoMatchData.setText(mMvnoMatchDataStr); 558 } 559 } 560 561 mName.setSummary(checkNull(mName.getText())); 562 mApn.setSummary(checkNull(mApn.getText())); 563 mProxy.setSummary(checkNull(mProxy.getText())); 564 mPort.setSummary(checkNull(mPort.getText())); 565 mUser.setSummary(checkNull(mUser.getText())); 566 mServer.setSummary(checkNull(mServer.getText())); 567 mPassword.setSummary(starify(mPassword.getText())); 568 mMmsProxy.setSummary(checkNull(mMmsProxy.getText())); 569 mMmsPort.setSummary(checkNull(mMmsPort.getText())); 570 mMmsc.setSummary(checkNull(mMmsc.getText())); 571 mMcc.setSummary(checkNull(mMcc.getText())); 572 mMnc.setSummary(checkNull(mMnc.getText())); 573 mApnType.setSummary(checkNull(mApnType.getText())); 574 575 String authVal = mAuthType.getValue(); 576 if (authVal != null) { 577 int authValIndex = Integer.parseInt(authVal); 578 mAuthType.setValueIndex(authValIndex); 579 580 String []values = mRes.getStringArray(R.array.apn_auth_entries); 581 mAuthType.setSummary(values[authValIndex]); 582 } else { 583 mAuthType.setSummary(sNotSet); 584 } 585 586 mProtocol.setSummary(checkNull(protocolDescription(mProtocol.getValue(), mProtocol))); 587 mRoamingProtocol.setSummary( 588 checkNull(protocolDescription(mRoamingProtocol.getValue(), mRoamingProtocol))); 589 mBearerMulti.setSummary( 590 checkNull(bearerMultiDescription(mBearerMulti.getValues()))); 591 mMvnoType.setSummary( 592 checkNull(mvnoDescription(mMvnoType.getValue()))); 593 mMvnoMatchData.setSummary(checkNull(mMvnoMatchData.getText())); 594 // allow user to edit carrier_enabled for some APN 595 boolean ceEditable = getResources().getBoolean(R.bool.config_allow_edit_carrier_enabled); 596 if (ceEditable) { 597 mCarrierEnabled.setEnabled(true); 598 } else { 599 mCarrierEnabled.setEnabled(false); 600 } 601 } 602 603 /** 604 * Returns the UI choice (e.g., "IPv4/IPv6") corresponding to the given 605 * raw value of the protocol preference (e.g., "IPV4V6"). If unknown, 606 * return null. 607 */ 608 private String protocolDescription(String raw, ListPreference protocol) { 609 int protocolIndex = protocol.findIndexOfValue(raw); 610 if (protocolIndex == -1) { 611 return null; 612 } else { 613 String[] values = mRes.getStringArray(R.array.apn_protocol_entries); 614 try { 615 return values[protocolIndex]; 616 } catch (ArrayIndexOutOfBoundsException e) { 617 return null; 618 } 619 } 620 } 621 622 private String bearerDescription(String raw) { 623 int mBearerIndex = mBearerMulti.findIndexOfValue(raw); 624 if (mBearerIndex == -1) { 625 return null; 626 } else { 627 String[] values = mRes.getStringArray(R.array.bearer_entries); 628 try { 629 return values[mBearerIndex]; 630 } catch (ArrayIndexOutOfBoundsException e) { 631 return null; 632 } 633 } 634 } 635 636 private String bearerMultiDescription(Set<String> raw) { 637 String[] values = mRes.getStringArray(R.array.bearer_entries); 638 StringBuilder retVal = new StringBuilder(); 639 boolean first = true; 640 for (String bearer : raw) { 641 int bearerIndex = mBearerMulti.findIndexOfValue(bearer); 642 try { 643 if (first) { 644 retVal.append(values[bearerIndex]); 645 first = false; 646 } else { 647 retVal.append(", " + values[bearerIndex]); 648 } 649 } catch (ArrayIndexOutOfBoundsException e) { 650 // ignore 651 } 652 } 653 String val = retVal.toString(); 654 if (!TextUtils.isEmpty(val)) { 655 return val; 656 } 657 return null; 658 } 659 660 private String mvnoDescription(String newValue) { 661 int mvnoIndex = mMvnoType.findIndexOfValue(newValue); 662 String oldValue = mMvnoType.getValue(); 663 664 if (mvnoIndex == -1) { 665 return null; 666 } else { 667 String[] values = mRes.getStringArray(R.array.mvno_type_entries); 668 boolean mvnoMatchDataUneditable = 669 mReadOnlyApn || (mReadOnlyApnFields != null 670 && Arrays.asList(mReadOnlyApnFields) 671 .contains(Telephony.Carriers.MVNO_MATCH_DATA)); 672 mMvnoMatchData.setEnabled(!mvnoMatchDataUneditable && mvnoIndex != 0); 673 if (newValue != null && newValue.equals(oldValue) == false) { 674 if (values[mvnoIndex].equals("SPN")) { 675 mMvnoMatchData.setText(mTelephonyManager.getSimOperatorName()); 676 } else if (values[mvnoIndex].equals("IMSI")) { 677 String numeric = mTelephonyManager.getSimOperator(mSubId); 678 mMvnoMatchData.setText(numeric + "x"); 679 } else if (values[mvnoIndex].equals("GID")) { 680 mMvnoMatchData.setText(mTelephonyManager.getGroupIdLevel1()); 681 } 682 } 683 684 try { 685 return values[mvnoIndex]; 686 } catch (ArrayIndexOutOfBoundsException e) { 687 return null; 688 } 689 } 690 } 691 692 public boolean onPreferenceChange(Preference preference, Object newValue) { 693 String key = preference.getKey(); 694 if (KEY_AUTH_TYPE.equals(key)) { 695 try { 696 int index = Integer.parseInt((String) newValue); 697 mAuthType.setValueIndex(index); 698 699 String[] values = mRes.getStringArray(R.array.apn_auth_entries); 700 mAuthType.setSummary(values[index]); 701 } catch (NumberFormatException e) { 702 return false; 703 } 704 } else if (KEY_PROTOCOL.equals(key)) { 705 String protocol = protocolDescription((String) newValue, mProtocol); 706 if (protocol == null) { 707 return false; 708 } 709 mProtocol.setSummary(protocol); 710 mProtocol.setValue((String) newValue); 711 } else if (KEY_ROAMING_PROTOCOL.equals(key)) { 712 String protocol = protocolDescription((String) newValue, mRoamingProtocol); 713 if (protocol == null) { 714 return false; 715 } 716 mRoamingProtocol.setSummary(protocol); 717 mRoamingProtocol.setValue((String) newValue); 718 } else if (KEY_BEARER_MULTI.equals(key)) { 719 String bearer = bearerMultiDescription((Set<String>) newValue); 720 if (bearer == null) { 721 return false; 722 } 723 mBearerMulti.setValues((Set<String>) newValue); 724 mBearerMulti.setSummary(bearer); 725 } else if (KEY_MVNO_TYPE.equals(key)) { 726 String mvno = mvnoDescription((String) newValue); 727 if (mvno == null) { 728 return false; 729 } 730 mMvnoType.setValue((String) newValue); 731 mMvnoType.setSummary(mvno); 732 } else if (KEY_PASSWORD.equals(key)) { 733 mPassword.setSummary(starify(newValue != null ? String.valueOf(newValue) : "")); 734 } else if (KEY_CARRIER_ENABLED.equals(key)) { 735 // do nothing 736 } else { 737 preference.setSummary(checkNull(newValue != null ? String.valueOf(newValue) : null)); 738 } 739 740 return true; 741 } 742 743 @Override 744 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 745 super.onCreateOptionsMenu(menu, inflater); 746 // If it's a new APN, then cancel will delete the new entry in onPause 747 if (!mNewApn && !mReadOnlyApn) { 748 menu.add(0, MENU_DELETE, 0, R.string.menu_delete) 749 .setIcon(R.drawable.ic_menu_delete); 750 } 751 menu.add(0, MENU_SAVE, 0, R.string.menu_save) 752 .setIcon(android.R.drawable.ic_menu_save); 753 menu.add(0, MENU_CANCEL, 0, R.string.menu_cancel) 754 .setIcon(android.R.drawable.ic_menu_close_clear_cancel); 755 } 756 757 @Override 758 public boolean onOptionsItemSelected(MenuItem item) { 759 switch (item.getItemId()) { 760 case MENU_DELETE: 761 deleteApn(); 762 return true; 763 case MENU_SAVE: 764 if (validateAndSave(false)) { 765 finish(); 766 } 767 return true; 768 case MENU_CANCEL: 769 if (mNewApn) { 770 getContentResolver().delete(mUri, null, null); 771 } 772 finish(); 773 return true; 774 } 775 return super.onOptionsItemSelected(item); 776 } 777 778 @Override 779 public void onViewCreated(View view, Bundle savedInstanceState) { 780 super.onViewCreated(view, savedInstanceState); 781 view.setOnKeyListener(this); 782 view.setFocusableInTouchMode(true); 783 view.requestFocus(); 784 } 785 786 public boolean onKey(View v, int keyCode, KeyEvent event) { 787 if (event.getAction() != KeyEvent.ACTION_DOWN) return false; 788 switch (keyCode) { 789 case KeyEvent.KEYCODE_BACK: { 790 if (validateAndSave(false)) { 791 finish(); 792 } 793 return true; 794 } 795 } 796 return false; 797 } 798 799 @Override 800 public void onSaveInstanceState(Bundle icicle) { 801 super.onSaveInstanceState(icicle); 802 if (validateAndSave(true)) { 803 icicle.putInt(SAVED_POS, mCursor.getInt(ID_INDEX)); 804 } 805 } 806 807 /** 808 * Add key, value to cv and compare the value against the value at index in mCursor. Return true 809 * if values are different. assumeDiff indicates if values can be assumed different in which 810 * case no comparison is needed. 811 * @return true if value is different from the value at index in mCursor 812 */ 813 boolean setStringValueAndCheckIfDiff(ContentValues cv, String key, String value, 814 boolean assumeDiff, int index) { 815 cv.put(key, value); 816 String valueFromCursor = mCursor.getString(index); 817 if (VDBG) { 818 Log.d(TAG, "setStringValueAndCheckIfDiff: assumeDiff: " + assumeDiff 819 + " key: " + key 820 + " value: '" + value 821 + "' valueFromCursor: '" + valueFromCursor + "'"); 822 } 823 return assumeDiff 824 || !((TextUtils.isEmpty(value) && TextUtils.isEmpty(valueFromCursor)) 825 || (value != null && value.equals(valueFromCursor))); 826 } 827 828 /** 829 * Add key, value to cv and compare the value against the value at index in mCursor. Return true 830 * if values are different. assumeDiff indicates if values can be assumed different in which 831 * case no comparison is needed. 832 * @return true if value is different from the value at index in mCursor 833 */ 834 boolean setIntValueAndCheckIfDiff(ContentValues cv, String key, int value, 835 boolean assumeDiff, int index) { 836 cv.put(key, value); 837 int valueFromCursor = mCursor.getInt(index); 838 if (VDBG) { 839 Log.d(TAG, "setIntValueAndCheckIfDiff: assumeDiff: " + assumeDiff 840 + " key: " + key 841 + " value: '" + value 842 + "' valueFromCursor: '" + valueFromCursor + "'"); 843 } 844 return assumeDiff || value != valueFromCursor; 845 } 846 847 /** 848 * Check the key fields' validity and save if valid. 849 * @param force save even if the fields are not valid, if the app is 850 * being suspended 851 * @return true if there's no error 852 */ 853 private boolean validateAndSave(boolean force) { 854 // nothing to do if it's a read only APN 855 if (mReadOnlyApn) { 856 return true; 857 } 858 859 String name = checkNotSet(mName.getText()); 860 String apn = checkNotSet(mApn.getText()); 861 String mcc = checkNotSet(mMcc.getText()); 862 String mnc = checkNotSet(mMnc.getText()); 863 864 if (getErrorMsg() != null && !force) { 865 ErrorDialog.showError(this); 866 return false; 867 } 868 869 if (!mCursor.moveToFirst()) { 870 Log.w(TAG, 871 "Could not go to the first row in the Cursor when saving data."); 872 return false; 873 } 874 875 // If it's a new APN and a name or apn haven't been entered, then erase the entry 876 if (force && mNewApn && name.length() < 1 && apn.length() < 1) { 877 getContentResolver().delete(mUri, null, null); 878 mUri = null; 879 return false; 880 } 881 882 ContentValues values = new ContentValues(); 883 // call update() if it's a new APN. If not, check if any field differs from the db value; 884 // if any diff is found update() should be called 885 boolean callUpdate = mNewApn; 886 887 // Add a dummy name "Untitled", if the user exits the screen without adding a name but 888 // entered other information worth keeping. 889 callUpdate = setStringValueAndCheckIfDiff(values, 890 Telephony.Carriers.NAME, 891 name.length() < 1 ? getResources().getString(R.string.untitled_apn) : name, 892 callUpdate, 893 NAME_INDEX); 894 895 callUpdate = setStringValueAndCheckIfDiff(values, 896 Telephony.Carriers.APN, 897 apn, 898 callUpdate, 899 APN_INDEX); 900 901 callUpdate = setStringValueAndCheckIfDiff(values, 902 Telephony.Carriers.PROXY, 903 checkNotSet(mProxy.getText()), 904 callUpdate, 905 PROXY_INDEX); 906 907 callUpdate = setStringValueAndCheckIfDiff(values, 908 Telephony.Carriers.PORT, 909 checkNotSet(mPort.getText()), 910 callUpdate, 911 PORT_INDEX); 912 913 callUpdate = setStringValueAndCheckIfDiff(values, 914 Telephony.Carriers.MMSPROXY, 915 checkNotSet(mMmsProxy.getText()), 916 callUpdate, 917 MMSPROXY_INDEX); 918 919 callUpdate = setStringValueAndCheckIfDiff(values, 920 Telephony.Carriers.MMSPORT, 921 checkNotSet(mMmsPort.getText()), 922 callUpdate, 923 MMSPORT_INDEX); 924 925 callUpdate = setStringValueAndCheckIfDiff(values, 926 Telephony.Carriers.USER, 927 checkNotSet(mUser.getText()), 928 callUpdate, 929 USER_INDEX); 930 931 callUpdate = setStringValueAndCheckIfDiff(values, 932 Telephony.Carriers.SERVER, 933 checkNotSet(mServer.getText()), 934 callUpdate, 935 SERVER_INDEX); 936 937 callUpdate = setStringValueAndCheckIfDiff(values, 938 Telephony.Carriers.PASSWORD, 939 checkNotSet(mPassword.getText()), 940 callUpdate, 941 PASSWORD_INDEX); 942 943 callUpdate = setStringValueAndCheckIfDiff(values, 944 Telephony.Carriers.MMSC, 945 checkNotSet(mMmsc.getText()), 946 callUpdate, 947 MMSC_INDEX); 948 949 String authVal = mAuthType.getValue(); 950 if (authVal != null) { 951 callUpdate = setIntValueAndCheckIfDiff(values, 952 Telephony.Carriers.AUTH_TYPE, 953 Integer.parseInt(authVal), 954 callUpdate, 955 AUTH_TYPE_INDEX); 956 } 957 958 callUpdate = setStringValueAndCheckIfDiff(values, 959 Telephony.Carriers.PROTOCOL, 960 checkNotSet(mProtocol.getValue()), 961 callUpdate, 962 PROTOCOL_INDEX); 963 964 callUpdate = setStringValueAndCheckIfDiff(values, 965 Telephony.Carriers.ROAMING_PROTOCOL, 966 checkNotSet(mRoamingProtocol.getValue()), 967 callUpdate, 968 ROAMING_PROTOCOL_INDEX); 969 970 callUpdate = setStringValueAndCheckIfDiff(values, 971 Telephony.Carriers.TYPE, 972 checkNotSet(getUserEnteredApnType()), 973 callUpdate, 974 TYPE_INDEX); 975 976 callUpdate = setStringValueAndCheckIfDiff(values, 977 Telephony.Carriers.MCC, 978 mcc, 979 callUpdate, 980 MCC_INDEX); 981 982 callUpdate = setStringValueAndCheckIfDiff(values, 983 Telephony.Carriers.MNC, 984 mnc, 985 callUpdate, 986 MNC_INDEX); 987 988 values.put(Telephony.Carriers.NUMERIC, mcc + mnc); 989 990 if (mCurMnc != null && mCurMcc != null) { 991 if (mCurMnc.equals(mnc) && mCurMcc.equals(mcc)) { 992 values.put(Telephony.Carriers.CURRENT, 1); 993 } 994 } 995 996 Set<String> bearerSet = mBearerMulti.getValues(); 997 int bearerBitmask = 0; 998 for (String bearer : bearerSet) { 999 if (Integer.parseInt(bearer) == 0) { 1000 bearerBitmask = 0; 1001 break; 1002 } else { 1003 bearerBitmask |= ServiceState.getBitmaskForTech(Integer.parseInt(bearer)); 1004 } 1005 } 1006 callUpdate = setIntValueAndCheckIfDiff(values, 1007 Telephony.Carriers.BEARER_BITMASK, 1008 bearerBitmask, 1009 callUpdate, 1010 BEARER_BITMASK_INDEX); 1011 1012 int bearerVal; 1013 if (bearerBitmask == 0 || mBearerInitialVal == 0) { 1014 bearerVal = 0; 1015 } else if (ServiceState.bitmaskHasTech(bearerBitmask, mBearerInitialVal)) { 1016 bearerVal = mBearerInitialVal; 1017 } else { 1018 // bearer field was being used but bitmask has changed now and does not include the 1019 // initial bearer value -- setting bearer to 0 but maybe better behavior is to choose a 1020 // random tech from the new bitmask?? 1021 bearerVal = 0; 1022 } 1023 callUpdate = setIntValueAndCheckIfDiff(values, 1024 Telephony.Carriers.BEARER, 1025 bearerVal, 1026 callUpdate, 1027 BEARER_INDEX); 1028 1029 callUpdate = setStringValueAndCheckIfDiff(values, 1030 Telephony.Carriers.MVNO_TYPE, 1031 checkNotSet(mMvnoType.getValue()), 1032 callUpdate, 1033 MVNO_TYPE_INDEX); 1034 1035 callUpdate = setStringValueAndCheckIfDiff(values, 1036 Telephony.Carriers.MVNO_MATCH_DATA, 1037 checkNotSet(mMvnoMatchData.getText()), 1038 callUpdate, 1039 MVNO_MATCH_DATA_INDEX); 1040 1041 callUpdate = setIntValueAndCheckIfDiff(values, 1042 Telephony.Carriers.CARRIER_ENABLED, 1043 mCarrierEnabled.isChecked() ? 1 : 0, 1044 callUpdate, 1045 CARRIER_ENABLED_INDEX); 1046 1047 if (callUpdate) { 1048 getContentResolver().update(mUri, values, null, null); 1049 } else { 1050 if (VDBG) Log.d(TAG, "validateAndSave: not calling update()"); 1051 } 1052 1053 return true; 1054 } 1055 1056 private String getErrorMsg() { 1057 String errorMsg = null; 1058 1059 String name = checkNotSet(mName.getText()); 1060 String apn = checkNotSet(mApn.getText()); 1061 String mcc = checkNotSet(mMcc.getText()); 1062 String mnc = checkNotSet(mMnc.getText()); 1063 1064 if (name.length() < 1) { 1065 errorMsg = mRes.getString(R.string.error_name_empty); 1066 } else if (apn.length() < 1) { 1067 errorMsg = mRes.getString(R.string.error_apn_empty); 1068 } else if (mcc.length() != 3) { 1069 errorMsg = mRes.getString(R.string.error_mcc_not3); 1070 } else if ((mnc.length() & 0xFFFE) != 2) { 1071 errorMsg = mRes.getString(R.string.error_mnc_not23); 1072 } 1073 1074 if (errorMsg == null) { 1075 // if carrier does not allow editing certain apn types, make sure type does not include 1076 // those 1077 if (!ArrayUtils.isEmpty(mReadOnlyApnTypes) 1078 && apnTypesMatch(mReadOnlyApnTypes, getUserEnteredApnType())) { 1079 StringBuilder stringBuilder = new StringBuilder(); 1080 for (String type : mReadOnlyApnTypes) { 1081 stringBuilder.append(type).append(", "); 1082 Log.d(TAG, "getErrorMsg: appending type: " + type); 1083 } 1084 // remove last ", " 1085 if (stringBuilder.length() >= 2) { 1086 stringBuilder.delete(stringBuilder.length() - 2, stringBuilder.length()); 1087 } 1088 errorMsg = String.format(mRes.getString(R.string.error_adding_apn_type), 1089 stringBuilder); 1090 } 1091 } 1092 1093 return errorMsg; 1094 } 1095 1096 private void deleteApn() { 1097 getContentResolver().delete(mUri, null, null); 1098 finish(); 1099 } 1100 1101 private String starify(String value) { 1102 if (value == null || value.length() == 0) { 1103 return sNotSet; 1104 } else { 1105 char[] password = new char[value.length()]; 1106 for (int i = 0; i < password.length; i++) { 1107 password[i] = '*'; 1108 } 1109 return new String(password); 1110 } 1111 } 1112 1113 private String checkNull(String value) { 1114 if (value == null || value.length() == 0) { 1115 return sNotSet; 1116 } else { 1117 return value; 1118 } 1119 } 1120 1121 private String checkNotSet(String value) { 1122 if (value == null || value.equals(sNotSet)) { 1123 return ""; 1124 } else { 1125 return value; 1126 } 1127 } 1128 1129 private String getUserEnteredApnType() { 1130 // if user has not specified a type, map it to "ALL APN TYPES THAT ARE NOT READ-ONLY" 1131 String userEnteredApnType = mApnType.getText(); 1132 if (userEnteredApnType != null) userEnteredApnType = userEnteredApnType.trim(); 1133 if ((TextUtils.isEmpty(userEnteredApnType) 1134 || PhoneConstants.APN_TYPE_ALL.equals(userEnteredApnType)) 1135 && !ArrayUtils.isEmpty(mReadOnlyApnTypes)) { 1136 StringBuilder editableApnTypes = new StringBuilder(); 1137 List<String> readOnlyApnTypes = Arrays.asList(mReadOnlyApnTypes); 1138 boolean first = true; 1139 for (String apnType : PhoneConstants.APN_TYPES) { 1140 // add APN type if it is not read-only and is not wild-cardable 1141 if (!readOnlyApnTypes.contains(apnType) 1142 && !apnType.equals(PhoneConstants.APN_TYPE_IA) 1143 && !apnType.equals(PhoneConstants.APN_TYPE_EMERGENCY)) { 1144 if (first) { 1145 first = false; 1146 } else { 1147 editableApnTypes.append(","); 1148 } 1149 editableApnTypes.append(apnType); 1150 } 1151 } 1152 userEnteredApnType = editableApnTypes.toString(); 1153 Log.d(TAG, "getUserEnteredApnType: changed apn type to editable apn types: " 1154 + userEnteredApnType); 1155 } 1156 1157 return userEnteredApnType; 1158 } 1159 1160 public static class ErrorDialog extends InstrumentedDialogFragment { 1161 1162 public static void showError(ApnEditor editor) { 1163 ErrorDialog dialog = new ErrorDialog(); 1164 dialog.setTargetFragment(editor, 0); 1165 dialog.show(editor.getFragmentManager(), "error"); 1166 } 1167 1168 @Override 1169 public Dialog onCreateDialog(Bundle savedInstanceState) { 1170 String msg = ((ApnEditor) getTargetFragment()).getErrorMsg(); 1171 1172 return new AlertDialog.Builder(getContext()) 1173 .setTitle(R.string.error_title) 1174 .setPositiveButton(android.R.string.ok, null) 1175 .setMessage(msg) 1176 .create(); 1177 } 1178 1179 @Override 1180 public int getMetricsCategory() { 1181 return MetricsEvent.DIALOG_APN_EDITOR_ERROR; 1182 } 1183 } 1184 1185} 1186