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