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.content.ContentUris; 21 import android.content.ContentValues; 22 import android.content.Intent; 23 import android.content.SharedPreferences; 24 import android.content.res.Resources; 25 import android.database.Cursor; 26 import android.net.Uri; 27 import android.os.Bundle; 28 import android.os.SystemProperties; 29 import android.preference.EditTextPreference; 30 import android.preference.ListPreference; 31 import android.preference.Preference; 32 import android.preference.PreferenceActivity; 33 import android.provider.Telephony; 34 import android.util.Log; 35 import android.view.KeyEvent; 36 import android.view.Menu; 37 import android.view.MenuItem; 38 39 import com.android.internal.telephony.TelephonyProperties; 40 import com.android.internal.telephony.RILConstants; 41 42 43 public class ApnEditor extends PreferenceActivity 44 implements SharedPreferences.OnSharedPreferenceChangeListener, 45 Preference.OnPreferenceChangeListener { 46 47 private final static String TAG = ApnEditor.class.getSimpleName(); 48 49 private final static String SAVED_POS = "pos"; 50 private final static String KEY_AUTH_TYPE = "auth_type"; 51 private final static String KEY_PROTOCOL = "apn_protocol"; 52 53 private static final int MENU_DELETE = Menu.FIRST; 54 private static final int MENU_SAVE = Menu.FIRST + 1; 55 private static final int MENU_CANCEL = Menu.FIRST + 2; 56 57 private static String sNotSet; 58 private EditTextPreference mName; 59 private EditTextPreference mApn; 60 private EditTextPreference mProxy; 61 private EditTextPreference mPort; 62 private EditTextPreference mUser; 63 private EditTextPreference mServer; 64 private EditTextPreference mPassword; 65 private EditTextPreference mMmsc; 66 private EditTextPreference mMcc; 67 private EditTextPreference mMnc; 68 private EditTextPreference mMmsProxy; 69 private EditTextPreference mMmsPort; 70 private ListPreference mAuthType; 71 private EditTextPreference mApnType; 72 private ListPreference mProtocol; 73 74 private String mCurMnc; 75 private String mCurMcc; 76 77 private Uri mUri; 78 private Cursor mCursor; 79 private boolean mNewApn; 80 private boolean mFirstTime; 81 private Resources mRes; 82 83 /** 84 * Standard projection for the interesting columns of a normal note. 85 */ 86 private static final String[] sProjection = new String[] { 87 Telephony.Carriers._ID, // 0 88 Telephony.Carriers.NAME, // 1 89 Telephony.Carriers.APN, // 2 90 Telephony.Carriers.PROXY, // 3 91 Telephony.Carriers.PORT, // 4 92 Telephony.Carriers.USER, // 5 93 Telephony.Carriers.SERVER, // 6 94 Telephony.Carriers.PASSWORD, // 7 95 Telephony.Carriers.MMSC, // 8 96 Telephony.Carriers.MCC, // 9 97 Telephony.Carriers.MNC, // 10 98 Telephony.Carriers.NUMERIC, // 11 99 Telephony.Carriers.MMSPROXY,// 12 100 Telephony.Carriers.MMSPORT, // 13 101 Telephony.Carriers.AUTH_TYPE, // 14 102 Telephony.Carriers.TYPE, // 15 103 Telephony.Carriers.PROTOCOL, // 16 104 }; 105 106 private static final int ID_INDEX = 0; 107 private static final int NAME_INDEX = 1; 108 private static final int APN_INDEX = 2; 109 private static final int PROXY_INDEX = 3; 110 private static final int PORT_INDEX = 4; 111 private static final int USER_INDEX = 5; 112 private static final int SERVER_INDEX = 6; 113 private static final int PASSWORD_INDEX = 7; 114 private static final int MMSC_INDEX = 8; 115 private static final int MCC_INDEX = 9; 116 private static final int MNC_INDEX = 10; 117 private static final int MMSPROXY_INDEX = 12; 118 private static final int MMSPORT_INDEX = 13; 119 private static final int AUTH_TYPE_INDEX = 14; 120 private static final int TYPE_INDEX = 15; 121 private static final int PROTOCOL_INDEX = 16; 122 123 124 @Override 125 protected void onCreate(Bundle icicle) { 126 super.onCreate(icicle); 127 128 addPreferencesFromResource(R.xml.apn_editor); 129 130 sNotSet = getResources().getString(R.string.apn_not_set); 131 mName = (EditTextPreference) findPreference("apn_name"); 132 mApn = (EditTextPreference) findPreference("apn_apn"); 133 mProxy = (EditTextPreference) findPreference("apn_http_proxy"); 134 mPort = (EditTextPreference) findPreference("apn_http_port"); 135 mUser = (EditTextPreference) findPreference("apn_user"); 136 mServer = (EditTextPreference) findPreference("apn_server"); 137 mPassword = (EditTextPreference) findPreference("apn_password"); 138 mMmsProxy = (EditTextPreference) findPreference("apn_mms_proxy"); 139 mMmsPort = (EditTextPreference) findPreference("apn_mms_port"); 140 mMmsc = (EditTextPreference) findPreference("apn_mmsc"); 141 mMcc = (EditTextPreference) findPreference("apn_mcc"); 142 mMnc = (EditTextPreference) findPreference("apn_mnc"); 143 mApnType = (EditTextPreference) findPreference("apn_type"); 144 145 mAuthType = (ListPreference) findPreference(KEY_AUTH_TYPE); 146 mAuthType.setOnPreferenceChangeListener(this); 147 148 mProtocol = (ListPreference) findPreference(KEY_PROTOCOL); 149 mProtocol.setOnPreferenceChangeListener(this); 150 151 mRes = getResources(); 152 153 final Intent intent = getIntent(); 154 final String action = intent.getAction(); 155 156 mFirstTime = icicle == null; 157 158 if (action.equals(Intent.ACTION_EDIT)) { 159 mUri = intent.getData(); 160 } else if (action.equals(Intent.ACTION_INSERT)) { 161 if (mFirstTime || icicle.getInt(SAVED_POS) == 0) { 162 mUri = getContentResolver().insert(intent.getData(), new ContentValues()); 163 } else { 164 mUri = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, 165 icicle.getInt(SAVED_POS)); 166 } 167 mNewApn = true; 168 // If we were unable to create a new note, then just finish 169 // this activity. A RESULT_CANCELED will be sent back to the 170 // original activity if they requested a result. 171 if (mUri == null) { 172 Log.w(TAG, "Failed to insert new telephony provider into " 173 + getIntent().getData()); 174 finish(); 175 return; 176 } 177 178 // The new entry was created, so assume all will end well and 179 // set the result to be returned. 180 setResult(RESULT_OK, (new Intent()).setAction(mUri.toString())); 181 182 } else { 183 finish(); 184 return; 185 } 186 187 mCursor = managedQuery(mUri, sProjection, null, null); 188 mCursor.moveToFirst(); 189 190 fillUi(); 191 } 192 193 @Override 194 public void onResume() { 195 super.onResume(); 196 getPreferenceScreen().getSharedPreferences() 197 .registerOnSharedPreferenceChangeListener(this); 198 } 199 200 @Override 201 public void onPause() { 202 getPreferenceScreen().getSharedPreferences() 203 .unregisterOnSharedPreferenceChangeListener(this); 204 super.onPause(); 205 } 206 207 private void fillUi() { 208 if (mFirstTime) { 209 mFirstTime = false; 210 // Fill in all the values from the db in both text editor and summary 211 mName.setText(mCursor.getString(NAME_INDEX)); 212 mApn.setText(mCursor.getString(APN_INDEX)); 213 mProxy.setText(mCursor.getString(PROXY_INDEX)); 214 mPort.setText(mCursor.getString(PORT_INDEX)); 215 mUser.setText(mCursor.getString(USER_INDEX)); 216 mServer.setText(mCursor.getString(SERVER_INDEX)); 217 mPassword.setText(mCursor.getString(PASSWORD_INDEX)); 218 mMmsProxy.setText(mCursor.getString(MMSPROXY_INDEX)); 219 mMmsPort.setText(mCursor.getString(MMSPORT_INDEX)); 220 mMmsc.setText(mCursor.getString(MMSC_INDEX)); 221 mMcc.setText(mCursor.getString(MCC_INDEX)); 222 mMnc.setText(mCursor.getString(MNC_INDEX)); 223 mApnType.setText(mCursor.getString(TYPE_INDEX)); 224 if (mNewApn) { 225 String numeric = 226 SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC); 227 // MCC is first 3 chars and then in 2 - 3 chars of MNC 228 if (numeric != null && numeric.length() > 4) { 229 // Country code 230 String mcc = numeric.substring(0, 3); 231 // Network code 232 String mnc = numeric.substring(3); 233 // Auto populate MNC and MCC for new entries, based on what SIM reports 234 mMcc.setText(mcc); 235 mMnc.setText(mnc); 236 mCurMnc = mnc; 237 mCurMcc = mcc; 238 } 239 } 240 int authVal = mCursor.getInt(AUTH_TYPE_INDEX); 241 if (authVal != -1) { 242 mAuthType.setValueIndex(authVal); 243 } 244 245 mProtocol.setValue(mCursor.getString(PROTOCOL_INDEX)); 246 } 247 248 mName.setSummary(checkNull(mName.getText())); 249 mApn.setSummary(checkNull(mApn.getText())); 250 mProxy.setSummary(checkNull(mProxy.getText())); 251 mPort.setSummary(checkNull(mPort.getText())); 252 mUser.setSummary(checkNull(mUser.getText())); 253 mServer.setSummary(checkNull(mServer.getText())); 254 mPassword.setSummary(starify(mPassword.getText())); 255 mMmsProxy.setSummary(checkNull(mMmsProxy.getText())); 256 mMmsPort.setSummary(checkNull(mMmsPort.getText())); 257 mMmsc.setSummary(checkNull(mMmsc.getText())); 258 mMcc.setSummary(checkNull(mMcc.getText())); 259 mMnc.setSummary(checkNull(mMnc.getText())); 260 mApnType.setSummary(checkNull(mApnType.getText())); 261 262 String authVal = mAuthType.getValue(); 263 if (authVal != null) { 264 int authValIndex = Integer.parseInt(authVal); 265 mAuthType.setValueIndex(authValIndex); 266 267 String []values = mRes.getStringArray(R.array.apn_auth_entries); 268 mAuthType.setSummary(values[authValIndex]); 269 } else { 270 mAuthType.setSummary(sNotSet); 271 } 272 273 mProtocol.setSummary( 274 checkNull(protocolDescription(mProtocol.getValue()))); 275 } 276 277 /** 278 * Returns the UI choice (e.g., "IPv4/IPv6") corresponding to the given 279 * raw value of the protocol preference (e.g., "IPV4V6"). If unknown, 280 * return null. 281 */ 282 private String protocolDescription(String raw) { 283 int protocolIndex = mProtocol.findIndexOfValue(raw); 284 if (protocolIndex == -1) { 285 return null; 286 } else { 287 String[] values = mRes.getStringArray(R.array.apn_protocol_entries); 288 try { 289 return values[protocolIndex]; 290 } catch (ArrayIndexOutOfBoundsException e) { 291 return null; 292 } 293 } 294 } 295 296 public boolean onPreferenceChange(Preference preference, Object newValue) { 297 String key = preference.getKey(); 298 if (KEY_AUTH_TYPE.equals(key)) { 299 try { 300 int index = Integer.parseInt((String) newValue); 301 mAuthType.setValueIndex(index); 302 303 String []values = mRes.getStringArray(R.array.apn_auth_entries); 304 mAuthType.setSummary(values[index]); 305 } catch (NumberFormatException e) { 306 return false; 307 } 308 return true; 309 } 310 311 if (KEY_PROTOCOL.equals(key)) { 312 String protocol = protocolDescription((String) newValue); 313 if (protocol == null) { 314 return false; 315 } 316 mProtocol.setSummary(protocol); 317 mProtocol.setValue((String) newValue); 318 } 319 return true; 320 } 321 322 @Override 323 public boolean onCreateOptionsMenu(Menu menu) { 324 super.onCreateOptionsMenu(menu); 325 // If it's a new APN, then cancel will delete the new entry in onPause 326 if (!mNewApn) { 327 menu.add(0, MENU_DELETE, 0, R.string.menu_delete) 328 .setIcon(android.R.drawable.ic_menu_delete); 329 } 330 menu.add(0, MENU_SAVE, 0, R.string.menu_save) 331 .setIcon(android.R.drawable.ic_menu_save); 332 menu.add(0, MENU_CANCEL, 0, R.string.menu_cancel) 333 .setIcon(android.R.drawable.ic_menu_close_clear_cancel); 334 return true; 335 } 336 337 @Override 338 public boolean onOptionsItemSelected(MenuItem item) { 339 switch (item.getItemId()) { 340 case MENU_DELETE: 341 deleteApn(); 342 return true; 343 case MENU_SAVE: 344 if (validateAndSave(false)) { 345 finish(); 346 } 347 return true; 348 case MENU_CANCEL: 349 if (mNewApn) { 350 getContentResolver().delete(mUri, null, null); 351 } 352 finish(); 353 return true; 354 } 355 return super.onOptionsItemSelected(item); 356 } 357 358 @Override 359 public boolean onKeyDown(int keyCode, KeyEvent event) { 360 switch (keyCode) { 361 case KeyEvent.KEYCODE_BACK: { 362 if (validateAndSave(false)) { 363 finish(); 364 } 365 return true; 366 } 367 } 368 return super.onKeyDown(keyCode, event); 369 } 370 371 @Override 372 protected void onSaveInstanceState(Bundle icicle) { 373 super.onSaveInstanceState(icicle); 374 if (validateAndSave(true)) { 375 icicle.putInt(SAVED_POS, mCursor.getInt(ID_INDEX)); 376 } 377 } 378 379 /** 380 * Check the key fields' validity and save if valid. 381 * @param force save even if the fields are not valid, if the app is 382 * being suspended 383 * @return true if the data was saved 384 */ 385 private boolean validateAndSave(boolean force) { 386 String name = checkNotSet(mName.getText()); 387 String apn = checkNotSet(mApn.getText()); 388 String mcc = checkNotSet(mMcc.getText()); 389 String mnc = checkNotSet(mMnc.getText()); 390 391 String errorMsg = null; 392 if (name.length() < 1) { 393 errorMsg = mRes.getString(R.string.error_name_empty); 394 } else if (apn.length() < 1) { 395 errorMsg = mRes.getString(R.string.error_apn_empty); 396 } else if (mcc.length() != 3) { 397 errorMsg = mRes.getString(R.string.error_mcc_not3); 398 } else if ((mnc.length() & 0xFFFE) != 2) { 399 errorMsg = mRes.getString(R.string.error_mnc_not23); 400 } 401 402 if (errorMsg != null && !force) { 403 showErrorMessage(errorMsg); 404 return false; 405 } 406 407 if (!mCursor.moveToFirst()) { 408 Log.w(TAG, 409 "Could not go to the first row in the Cursor when saving data."); 410 return false; 411 } 412 413 // If it's a new APN and a name or apn haven't been entered, then erase the entry 414 if (force && mNewApn && name.length() < 1 && apn.length() < 1) { 415 getContentResolver().delete(mUri, null, null); 416 return false; 417 } 418 419 ContentValues values = new ContentValues(); 420 421 // Add a dummy name "Untitled", if the user exits the screen without adding a name but 422 // entered other information worth keeping. 423 values.put(Telephony.Carriers.NAME, 424 name.length() < 1 ? getResources().getString(R.string.untitled_apn) : name); 425 values.put(Telephony.Carriers.APN, apn); 426 values.put(Telephony.Carriers.PROXY, checkNotSet(mProxy.getText())); 427 values.put(Telephony.Carriers.PORT, checkNotSet(mPort.getText())); 428 values.put(Telephony.Carriers.MMSPROXY, checkNotSet(mMmsProxy.getText())); 429 values.put(Telephony.Carriers.MMSPORT, checkNotSet(mMmsPort.getText())); 430 values.put(Telephony.Carriers.USER, checkNotSet(mUser.getText())); 431 values.put(Telephony.Carriers.SERVER, checkNotSet(mServer.getText())); 432 values.put(Telephony.Carriers.PASSWORD, checkNotSet(mPassword.getText())); 433 values.put(Telephony.Carriers.MMSC, checkNotSet(mMmsc.getText())); 434 435 String authVal = mAuthType.getValue(); 436 if (authVal != null) { 437 values.put(Telephony.Carriers.AUTH_TYPE, Integer.parseInt(authVal)); 438 } 439 440 values.put(Telephony.Carriers.PROTOCOL, checkNotSet(mProtocol.getValue())); 441 442 // Hardcode IPv4 roaming for now until the carriers sort out all the 443 // billing arrangements. 444 values.put(Telephony.Carriers.ROAMING_PROTOCOL, 445 RILConstants.SETUP_DATA_PROTOCOL_IP); 446 447 values.put(Telephony.Carriers.TYPE, checkNotSet(mApnType.getText())); 448 449 values.put(Telephony.Carriers.MCC, mcc); 450 values.put(Telephony.Carriers.MNC, mnc); 451 452 values.put(Telephony.Carriers.NUMERIC, mcc + mnc); 453 454 if (mCurMnc != null && mCurMcc != null) { 455 if (mCurMnc.equals(mnc) && mCurMcc.equals(mcc)) { 456 values.put(Telephony.Carriers.CURRENT, 1); 457 } 458 } 459 460 getContentResolver().update(mUri, values, null, null); 461 462 return true; 463 } 464 465 private void showErrorMessage(String message) { 466 new AlertDialog.Builder(this) 467 .setTitle(R.string.error_title) 468 .setMessage(message) 469 .setPositiveButton(android.R.string.ok, null) 470 .show(); 471 } 472 473 private void deleteApn() { 474 getContentResolver().delete(mUri, null, null); 475 finish(); 476 } 477 478 private String starify(String value) { 479 if (value == null || value.length() == 0) { 480 return sNotSet; 481 } else { 482 char[] password = new char[value.length()]; 483 for (int i = 0; i < password.length; i++) { 484 password[i] = '*'; 485 } 486 return new String(password); 487 } 488 } 489 490 private String checkNull(String value) { 491 if (value == null || value.length() == 0) { 492 return sNotSet; 493 } else { 494 return value; 495 } 496 } 497 498 private String checkNotSet(String value) { 499 if (value == null || value.equals(sNotSet)) { 500 return ""; 501 } else { 502 return value; 503 } 504 } 505 506 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 507 Preference pref = findPreference(key); 508 if (pref != null) { 509 if (pref.equals(mPassword)){ 510 pref.setSummary(starify(sharedPreferences.getString(key, ""))); 511 } else { 512 pref.setSummary(checkNull(sharedPreferences.getString(key, ""))); 513 } 514 } 515 } 516 } 517