1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings; 18 19 import android.app.AlertDialog; 20 import android.app.Dialog; 21 import android.content.ContentUris; 22 import android.content.ContentValues; 23 import android.content.Intent; 24 import android.content.SharedPreferences; 25 import android.content.res.Resources; 26 import android.database.Cursor; 27 import android.net.Uri; 28 import android.os.Bundle; 29 import android.os.SystemProperties; 30 import android.preference.EditTextPreference; 31 import android.preference.ListPreference; 32 import android.preference.CheckBoxPreference; 33 import android.preference.Preference; 34 import android.preference.PreferenceActivity; 35 import android.provider.Telephony; 36 import android.telephony.TelephonyManager; 37 import android.util.Log; 38 import android.view.KeyEvent; 39 import android.view.Menu; 40 import android.view.MenuItem; 41 42 import com.android.internal.telephony.Phone; 43 import com.android.internal.telephony.RILConstants; 44 import com.android.internal.telephony.TelephonyProperties; 45 46 47 public class ApnEditor extends PreferenceActivity 48 implements SharedPreferences.OnSharedPreferenceChangeListener, 49 Preference.OnPreferenceChangeListener { 50 51 private final static String TAG = ApnEditor.class.getSimpleName(); 52 53 private final static String SAVED_POS = "pos"; 54 private final static String KEY_AUTH_TYPE = "auth_type"; 55 private final static String KEY_PROTOCOL = "apn_protocol"; 56 private final static String KEY_ROAMING_PROTOCOL = "apn_roaming_protocol"; 57 private final static String KEY_CARRIER_ENABLED = "carrier_enabled"; 58 private final static String KEY_BEARER = "bearer"; 59 60 private static final int MENU_DELETE = Menu.FIRST; 61 private static final int MENU_SAVE = Menu.FIRST + 1; 62 private static final int MENU_CANCEL = Menu.FIRST + 2; 63 private static final int ERROR_DIALOG_ID = 0; 64 65 private static String sNotSet; 66 private EditTextPreference mName; 67 private EditTextPreference mApn; 68 private EditTextPreference mProxy; 69 private EditTextPreference mPort; 70 private EditTextPreference mUser; 71 private EditTextPreference mServer; 72 private EditTextPreference mPassword; 73 private EditTextPreference mMmsc; 74 private EditTextPreference mMcc; 75 private EditTextPreference mMnc; 76 private EditTextPreference mMmsProxy; 77 private EditTextPreference mMmsPort; 78 private ListPreference mAuthType; 79 private EditTextPreference mApnType; 80 private ListPreference mProtocol; 81 private ListPreference mRoamingProtocol; 82 private CheckBoxPreference mCarrierEnabled; 83 private ListPreference mBearer; 84 85 private String mCurMnc; 86 private String mCurMcc; 87 88 private Uri mUri; 89 private Cursor mCursor; 90 private boolean mNewApn; 91 private boolean mFirstTime; 92 private Resources mRes; 93 94 /** 95 * Standard projection for the interesting columns of a normal note. 96 */ 97 private static final String[] sProjection = new String[] { 98 Telephony.Carriers._ID, // 0 99 Telephony.Carriers.NAME, // 1 100 Telephony.Carriers.APN, // 2 101 Telephony.Carriers.PROXY, // 3 102 Telephony.Carriers.PORT, // 4 103 Telephony.Carriers.USER, // 5 104 Telephony.Carriers.SERVER, // 6 105 Telephony.Carriers.PASSWORD, // 7 106 Telephony.Carriers.MMSC, // 8 107 Telephony.Carriers.MCC, // 9 108 Telephony.Carriers.MNC, // 10 109 Telephony.Carriers.NUMERIC, // 11 110 Telephony.Carriers.MMSPROXY,// 12 111 Telephony.Carriers.MMSPORT, // 13 112 Telephony.Carriers.AUTH_TYPE, // 14 113 Telephony.Carriers.TYPE, // 15 114 Telephony.Carriers.PROTOCOL, // 16 115 Telephony.Carriers.CARRIER_ENABLED, // 17 116 Telephony.Carriers.BEARER, // 18 117 Telephony.Carriers.ROAMING_PROTOCOL // 19 118 }; 119 120 private static final int ID_INDEX = 0; 121 private static final int NAME_INDEX = 1; 122 private static final int APN_INDEX = 2; 123 private static final int PROXY_INDEX = 3; 124 private static final int PORT_INDEX = 4; 125 private static final int USER_INDEX = 5; 126 private static final int SERVER_INDEX = 6; 127 private static final int PASSWORD_INDEX = 7; 128 private static final int MMSC_INDEX = 8; 129 private static final int MCC_INDEX = 9; 130 private static final int MNC_INDEX = 10; 131 private static final int MMSPROXY_INDEX = 12; 132 private static final int MMSPORT_INDEX = 13; 133 private static final int AUTH_TYPE_INDEX = 14; 134 private static final int TYPE_INDEX = 15; 135 private static final int PROTOCOL_INDEX = 16; 136 private static final int CARRIER_ENABLED_INDEX = 17; 137 private static final int BEARER_INDEX = 18; 138 private static final int ROAMING_PROTOCOL_INDEX = 19; 139 140 141 @Override 142 protected void onCreate(Bundle icicle) { 143 super.onCreate(icicle); 144 145 addPreferencesFromResource(R.xml.apn_editor); 146 147 sNotSet = getResources().getString(R.string.apn_not_set); 148 mName = (EditTextPreference) findPreference("apn_name"); 149 mApn = (EditTextPreference) findPreference("apn_apn"); 150 mProxy = (EditTextPreference) findPreference("apn_http_proxy"); 151 mPort = (EditTextPreference) findPreference("apn_http_port"); 152 mUser = (EditTextPreference) findPreference("apn_user"); 153 mServer = (EditTextPreference) findPreference("apn_server"); 154 mPassword = (EditTextPreference) findPreference("apn_password"); 155 mMmsProxy = (EditTextPreference) findPreference("apn_mms_proxy"); 156 mMmsPort = (EditTextPreference) findPreference("apn_mms_port"); 157 mMmsc = (EditTextPreference) findPreference("apn_mmsc"); 158 mMcc = (EditTextPreference) findPreference("apn_mcc"); 159 mMnc = (EditTextPreference) findPreference("apn_mnc"); 160 mApnType = (EditTextPreference) findPreference("apn_type"); 161 162 mAuthType = (ListPreference) findPreference(KEY_AUTH_TYPE); 163 mAuthType.setOnPreferenceChangeListener(this); 164 165 mProtocol = (ListPreference) findPreference(KEY_PROTOCOL); 166 mProtocol.setOnPreferenceChangeListener(this); 167 168 mRoamingProtocol = (ListPreference) findPreference(KEY_ROAMING_PROTOCOL); 169 // Only enable this on CDMA phones for now, since it may cause problems on other phone 170 // types. (This screen is not normally accessible on CDMA phones, but is useful for 171 // testing.) 172 TelephonyManager tm = (TelephonyManager)getSystemService(TELEPHONY_SERVICE); 173 if (tm.getCurrentPhoneType() == Phone.PHONE_TYPE_CDMA) { 174 mRoamingProtocol.setOnPreferenceChangeListener(this); 175 } else { 176 getPreferenceScreen().removePreference(mRoamingProtocol); 177 } 178 179 mCarrierEnabled = (CheckBoxPreference) findPreference(KEY_CARRIER_ENABLED); 180 181 mBearer = (ListPreference) findPreference(KEY_BEARER); 182 mBearer.setOnPreferenceChangeListener(this); 183 184 mRes = getResources(); 185 186 final Intent intent = getIntent(); 187 final String action = intent.getAction(); 188 189 mFirstTime = icicle == null; 190 191 if (action.equals(Intent.ACTION_EDIT)) { 192 mUri = intent.getData(); 193 } else if (action.equals(Intent.ACTION_INSERT)) { 194 if (mFirstTime || icicle.getInt(SAVED_POS) == 0) { 195 mUri = getContentResolver().insert(intent.getData(), new ContentValues()); 196 } else { 197 mUri = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, 198 icicle.getInt(SAVED_POS)); 199 } 200 mNewApn = true; 201 // If we were unable to create a new note, then just finish 202 // this activity. A RESULT_CANCELED will be sent back to the 203 // original activity if they requested a result. 204 if (mUri == null) { 205 Log.w(TAG, "Failed to insert new telephony provider into " 206 + getIntent().getData()); 207 finish(); 208 return; 209 } 210 211 // The new entry was created, so assume all will end well and 212 // set the result to be returned. 213 setResult(RESULT_OK, (new Intent()).setAction(mUri.toString())); 214 215 } else { 216 finish(); 217 return; 218 } 219 220 mCursor = managedQuery(mUri, sProjection, null, null); 221 mCursor.moveToFirst(); 222 223 fillUi(); 224 } 225 226 @Override 227 public void onResume() { 228 super.onResume(); 229 getPreferenceScreen().getSharedPreferences() 230 .registerOnSharedPreferenceChangeListener(this); 231 } 232 233 @Override 234 public void onPause() { 235 getPreferenceScreen().getSharedPreferences() 236 .unregisterOnSharedPreferenceChangeListener(this); 237 super.onPause(); 238 } 239 240 private void fillUi() { 241 if (mFirstTime) { 242 mFirstTime = false; 243 // Fill in all the values from the db in both text editor and summary 244 mName.setText(mCursor.getString(NAME_INDEX)); 245 mApn.setText(mCursor.getString(APN_INDEX)); 246 mProxy.setText(mCursor.getString(PROXY_INDEX)); 247 mPort.setText(mCursor.getString(PORT_INDEX)); 248 mUser.setText(mCursor.getString(USER_INDEX)); 249 mServer.setText(mCursor.getString(SERVER_INDEX)); 250 mPassword.setText(mCursor.getString(PASSWORD_INDEX)); 251 mMmsProxy.setText(mCursor.getString(MMSPROXY_INDEX)); 252 mMmsPort.setText(mCursor.getString(MMSPORT_INDEX)); 253 mMmsc.setText(mCursor.getString(MMSC_INDEX)); 254 mMcc.setText(mCursor.getString(MCC_INDEX)); 255 mMnc.setText(mCursor.getString(MNC_INDEX)); 256 mApnType.setText(mCursor.getString(TYPE_INDEX)); 257 if (mNewApn) { 258 String numeric = 259 SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC); 260 // MCC is first 3 chars and then in 2 - 3 chars of MNC 261 if (numeric != null && numeric.length() > 4) { 262 // Country code 263 String mcc = numeric.substring(0, 3); 264 // Network code 265 String mnc = numeric.substring(3); 266 // Auto populate MNC and MCC for new entries, based on what SIM reports 267 mMcc.setText(mcc); 268 mMnc.setText(mnc); 269 mCurMnc = mnc; 270 mCurMcc = mcc; 271 } 272 } 273 int authVal = mCursor.getInt(AUTH_TYPE_INDEX); 274 if (authVal != -1) { 275 mAuthType.setValueIndex(authVal); 276 } else { 277 mAuthType.setValue(null); 278 } 279 280 mProtocol.setValue(mCursor.getString(PROTOCOL_INDEX)); 281 mRoamingProtocol.setValue(mCursor.getString(ROAMING_PROTOCOL_INDEX)); 282 mCarrierEnabled.setChecked(mCursor.getInt(CARRIER_ENABLED_INDEX)==1); 283 mBearer.setValue(mCursor.getString(BEARER_INDEX)); 284 } 285 286 mName.setSummary(checkNull(mName.getText())); 287 mApn.setSummary(checkNull(mApn.getText())); 288 mProxy.setSummary(checkNull(mProxy.getText())); 289 mPort.setSummary(checkNull(mPort.getText())); 290 mUser.setSummary(checkNull(mUser.getText())); 291 mServer.setSummary(checkNull(mServer.getText())); 292 mPassword.setSummary(starify(mPassword.getText())); 293 mMmsProxy.setSummary(checkNull(mMmsProxy.getText())); 294 mMmsPort.setSummary(checkNull(mMmsPort.getText())); 295 mMmsc.setSummary(checkNull(mMmsc.getText())); 296 mMcc.setSummary(checkNull(mMcc.getText())); 297 mMnc.setSummary(checkNull(mMnc.getText())); 298 mApnType.setSummary(checkNull(mApnType.getText())); 299 300 String authVal = mAuthType.getValue(); 301 if (authVal != null) { 302 int authValIndex = Integer.parseInt(authVal); 303 mAuthType.setValueIndex(authValIndex); 304 305 String []values = mRes.getStringArray(R.array.apn_auth_entries); 306 mAuthType.setSummary(values[authValIndex]); 307 } else { 308 mAuthType.setSummary(sNotSet); 309 } 310 311 mProtocol.setSummary( 312 checkNull(protocolDescription(mProtocol.getValue(), mProtocol))); 313 mRoamingProtocol.setSummary( 314 checkNull(protocolDescription(mRoamingProtocol.getValue(), mRoamingProtocol))); 315 mBearer.setSummary( 316 checkNull(bearerDescription(mBearer.getValue()))); 317 } 318 319 /** 320 * Returns the UI choice (e.g., "IPv4/IPv6") corresponding to the given 321 * raw value of the protocol preference (e.g., "IPV4V6"). If unknown, 322 * return null. 323 */ 324 private String protocolDescription(String raw, ListPreference protocol) { 325 int protocolIndex = protocol.findIndexOfValue(raw); 326 if (protocolIndex == -1) { 327 return null; 328 } else { 329 String[] values = mRes.getStringArray(R.array.apn_protocol_entries); 330 try { 331 return values[protocolIndex]; 332 } catch (ArrayIndexOutOfBoundsException e) { 333 return null; 334 } 335 } 336 } 337 338 private String bearerDescription(String raw) { 339 int mBearerIndex = mBearer.findIndexOfValue(raw); 340 if (mBearerIndex == -1) { 341 return null; 342 } else { 343 String[] values = mRes.getStringArray(R.array.bearer_entries); 344 try { 345 return values[mBearerIndex]; 346 } catch (ArrayIndexOutOfBoundsException e) { 347 return null; 348 } 349 } 350 } 351 352 public boolean onPreferenceChange(Preference preference, Object newValue) { 353 String key = preference.getKey(); 354 if (KEY_AUTH_TYPE.equals(key)) { 355 try { 356 int index = Integer.parseInt((String) newValue); 357 mAuthType.setValueIndex(index); 358 359 String []values = mRes.getStringArray(R.array.apn_auth_entries); 360 mAuthType.setSummary(values[index]); 361 } catch (NumberFormatException e) { 362 return false; 363 } 364 } else if (KEY_PROTOCOL.equals(key)) { 365 String protocol = protocolDescription((String) newValue, mProtocol); 366 if (protocol == null) { 367 return false; 368 } 369 mProtocol.setSummary(protocol); 370 mProtocol.setValue((String) newValue); 371 } else if (KEY_ROAMING_PROTOCOL.equals(key)) { 372 String protocol = protocolDescription((String) newValue, mRoamingProtocol); 373 if (protocol == null) { 374 return false; 375 } 376 mRoamingProtocol.setSummary(protocol); 377 mRoamingProtocol.setValue((String) newValue); 378 } else if (KEY_BEARER.equals(key)) { 379 String bearer = bearerDescription((String) newValue); 380 if (bearer == null) { 381 return false; 382 } 383 mBearer.setValue((String) newValue); 384 mBearer.setSummary(bearer); 385 } 386 387 return true; 388 } 389 390 @Override 391 public boolean onCreateOptionsMenu(Menu menu) { 392 super.onCreateOptionsMenu(menu); 393 // If it's a new APN, then cancel will delete the new entry in onPause 394 if (!mNewApn) { 395 menu.add(0, MENU_DELETE, 0, R.string.menu_delete) 396 .setIcon(R.drawable.ic_menu_delete_holo_dark); 397 } 398 menu.add(0, MENU_SAVE, 0, R.string.menu_save) 399 .setIcon(android.R.drawable.ic_menu_save); 400 menu.add(0, MENU_CANCEL, 0, R.string.menu_cancel) 401 .setIcon(android.R.drawable.ic_menu_close_clear_cancel); 402 return true; 403 } 404 405 @Override 406 public boolean onOptionsItemSelected(MenuItem item) { 407 switch (item.getItemId()) { 408 case MENU_DELETE: 409 deleteApn(); 410 return true; 411 case MENU_SAVE: 412 if (validateAndSave(false)) { 413 finish(); 414 } 415 return true; 416 case MENU_CANCEL: 417 if (mNewApn) { 418 getContentResolver().delete(mUri, null, null); 419 } 420 finish(); 421 return true; 422 } 423 return super.onOptionsItemSelected(item); 424 } 425 426 @Override 427 public boolean onKeyDown(int keyCode, KeyEvent event) { 428 switch (keyCode) { 429 case KeyEvent.KEYCODE_BACK: { 430 if (validateAndSave(false)) { 431 finish(); 432 } 433 return true; 434 } 435 } 436 return super.onKeyDown(keyCode, event); 437 } 438 439 @Override 440 protected void onSaveInstanceState(Bundle icicle) { 441 super.onSaveInstanceState(icicle); 442 if (validateAndSave(true)) { 443 icicle.putInt(SAVED_POS, mCursor.getInt(ID_INDEX)); 444 } 445 } 446 447 /** 448 * Check the key fields' validity and save if valid. 449 * @param force save even if the fields are not valid, if the app is 450 * being suspended 451 * @return true if the data was saved 452 */ 453 private boolean validateAndSave(boolean force) { 454 String name = checkNotSet(mName.getText()); 455 String apn = checkNotSet(mApn.getText()); 456 String mcc = checkNotSet(mMcc.getText()); 457 String mnc = checkNotSet(mMnc.getText()); 458 459 if (getErrorMsg() != null && !force) { 460 showDialog(ERROR_DIALOG_ID); 461 return false; 462 } 463 464 if (!mCursor.moveToFirst()) { 465 Log.w(TAG, 466 "Could not go to the first row in the Cursor when saving data."); 467 return false; 468 } 469 470 // If it's a new APN and a name or apn haven't been entered, then erase the entry 471 if (force && mNewApn && name.length() < 1 && apn.length() < 1) { 472 getContentResolver().delete(mUri, null, null); 473 return false; 474 } 475 476 ContentValues values = new ContentValues(); 477 478 // Add a dummy name "Untitled", if the user exits the screen without adding a name but 479 // entered other information worth keeping. 480 values.put(Telephony.Carriers.NAME, 481 name.length() < 1 ? getResources().getString(R.string.untitled_apn) : name); 482 values.put(Telephony.Carriers.APN, apn); 483 values.put(Telephony.Carriers.PROXY, checkNotSet(mProxy.getText())); 484 values.put(Telephony.Carriers.PORT, checkNotSet(mPort.getText())); 485 values.put(Telephony.Carriers.MMSPROXY, checkNotSet(mMmsProxy.getText())); 486 values.put(Telephony.Carriers.MMSPORT, checkNotSet(mMmsPort.getText())); 487 values.put(Telephony.Carriers.USER, checkNotSet(mUser.getText())); 488 values.put(Telephony.Carriers.SERVER, checkNotSet(mServer.getText())); 489 values.put(Telephony.Carriers.PASSWORD, checkNotSet(mPassword.getText())); 490 values.put(Telephony.Carriers.MMSC, checkNotSet(mMmsc.getText())); 491 492 String authVal = mAuthType.getValue(); 493 if (authVal != null) { 494 values.put(Telephony.Carriers.AUTH_TYPE, Integer.parseInt(authVal)); 495 } 496 497 values.put(Telephony.Carriers.PROTOCOL, checkNotSet(mProtocol.getValue())); 498 values.put(Telephony.Carriers.ROAMING_PROTOCOL, checkNotSet(mRoamingProtocol.getValue())); 499 500 values.put(Telephony.Carriers.TYPE, checkNotSet(mApnType.getText())); 501 502 values.put(Telephony.Carriers.MCC, mcc); 503 values.put(Telephony.Carriers.MNC, mnc); 504 505 values.put(Telephony.Carriers.NUMERIC, mcc + mnc); 506 507 if (mCurMnc != null && mCurMcc != null) { 508 if (mCurMnc.equals(mnc) && mCurMcc.equals(mcc)) { 509 values.put(Telephony.Carriers.CURRENT, 1); 510 } 511 } 512 513 String bearerVal = mBearer.getValue(); 514 if (bearerVal != null) { 515 values.put(Telephony.Carriers.BEARER, Integer.parseInt(bearerVal)); 516 } 517 518 getContentResolver().update(mUri, values, null, null); 519 520 return true; 521 } 522 523 private String getErrorMsg() { 524 String errorMsg = null; 525 526 String name = checkNotSet(mName.getText()); 527 String apn = checkNotSet(mApn.getText()); 528 String mcc = checkNotSet(mMcc.getText()); 529 String mnc = checkNotSet(mMnc.getText()); 530 531 if (name.length() < 1) { 532 errorMsg = mRes.getString(R.string.error_name_empty); 533 } else if (apn.length() < 1) { 534 errorMsg = mRes.getString(R.string.error_apn_empty); 535 } else if (mcc.length() != 3) { 536 errorMsg = mRes.getString(R.string.error_mcc_not3); 537 } else if ((mnc.length() & 0xFFFE) != 2) { 538 errorMsg = mRes.getString(R.string.error_mnc_not23); 539 } 540 541 return errorMsg; 542 } 543 544 @Override 545 protected Dialog onCreateDialog(int id) { 546 547 if (id == ERROR_DIALOG_ID) { 548 String msg = getErrorMsg(); 549 550 return new AlertDialog.Builder(this) 551 .setTitle(R.string.error_title) 552 .setPositiveButton(android.R.string.ok, null) 553 .setMessage(msg) 554 .create(); 555 } 556 557 return super.onCreateDialog(id); 558 } 559 560 @Override 561 protected void onPrepareDialog(int id, Dialog dialog) { 562 super.onPrepareDialog(id, dialog); 563 564 if (id == ERROR_DIALOG_ID) { 565 String msg = getErrorMsg(); 566 567 if (msg != null) { 568 ((AlertDialog)dialog).setMessage(msg); 569 } 570 } 571 } 572 573 private void deleteApn() { 574 getContentResolver().delete(mUri, null, null); 575 finish(); 576 } 577 578 private String starify(String value) { 579 if (value == null || value.length() == 0) { 580 return sNotSet; 581 } else { 582 char[] password = new char[value.length()]; 583 for (int i = 0; i < password.length; i++) { 584 password[i] = '*'; 585 } 586 return new String(password); 587 } 588 } 589 590 private String checkNull(String value) { 591 if (value == null || value.length() == 0) { 592 return sNotSet; 593 } else { 594 return value; 595 } 596 } 597 598 private String checkNotSet(String value) { 599 if (value == null || value.equals(sNotSet)) { 600 return ""; 601 } else { 602 return value; 603 } 604 } 605 606 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 607 Preference pref = findPreference(key); 608 if (pref != null) { 609 if (pref.equals(mPassword)){ 610 pref.setSummary(starify(sharedPreferences.getString(key, ""))); 611 } else { 612 pref.setSummary(checkNull(sharedPreferences.getString(key, ""))); 613 } 614 } 615 } 616 } 617