FdnSetting.java revision 83383e4c2f748514124eaa0482091490c4c8a311
1/* 2 * Copyright (C) 2008 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.phone.settings.fdn; 18 19import android.app.ActionBar; 20import android.app.AlertDialog; 21import android.content.DialogInterface; 22import android.os.AsyncResult; 23import android.os.Bundle; 24import android.os.Handler; 25import android.os.Message; 26import android.util.Log; 27import android.preference.PreferenceActivity; 28import android.preference.PreferenceScreen; 29import android.view.MenuItem; 30import android.view.WindowManager; 31import android.widget.Toast; 32 33import com.android.internal.telephony.CommandException; 34import com.android.internal.telephony.Phone; 35import com.android.phone.CallFeaturesSetting; 36import com.android.phone.PhoneGlobals; 37import com.android.phone.R; 38 39/** 40 * FDN settings UI for the Phone app. 41 * Rewritten to look and behave closer to the other preferences. 42 */ 43public class FdnSetting extends PreferenceActivity 44 implements EditPinPreference.OnPinEnteredListener, DialogInterface.OnCancelListener { 45 46 private static final String LOG_TAG = PhoneGlobals.LOG_TAG; 47 private static final boolean DBG = false; 48 49 private Phone mPhone; 50 51 /** 52 * Events we handle. 53 * The first is used for toggling FDN enable, the second for the PIN change. 54 */ 55 private static final int EVENT_PIN2_ENTRY_COMPLETE = 100; 56 private static final int EVENT_PIN2_CHANGE_COMPLETE = 200; 57 58 // String keys for preference lookup 59 // We only care about the pin preferences here, the manage FDN contacts 60 // Preference is handled solely in xml. 61 private static final String BUTTON_FDN_ENABLE_KEY = "button_fdn_enable_key"; 62 private static final String BUTTON_CHANGE_PIN2_KEY = "button_change_pin2_key"; 63 64 private EditPinPreference mButtonEnableFDN; 65 private EditPinPreference mButtonChangePin2; 66 67 // State variables 68 private String mOldPin; 69 private String mNewPin; 70 private String mPuk2; 71 private static final int PIN_CHANGE_OLD = 0; 72 private static final int PIN_CHANGE_NEW = 1; 73 private static final int PIN_CHANGE_REENTER = 2; 74 private static final int PIN_CHANGE_PUK = 3; 75 private static final int PIN_CHANGE_NEW_PIN_FOR_PUK = 4; 76 private static final int PIN_CHANGE_REENTER_PIN_FOR_PUK = 5; 77 private int mPinChangeState; 78 private boolean mIsPuk2Locked; // Indicates we know that we are PUK2 blocked. 79 80 private static final String SKIP_OLD_PIN_KEY = "skip_old_pin_key"; 81 private static final String PIN_CHANGE_STATE_KEY = "pin_change_state_key"; 82 private static final String OLD_PIN_KEY = "old_pin_key"; 83 private static final String NEW_PIN_KEY = "new_pin_key"; 84 private static final String DIALOG_MESSAGE_KEY = "dialog_message_key"; 85 private static final String DIALOG_PIN_ENTRY_KEY = "dialog_pin_entry_key"; 86 87 // size limits for the pin. 88 private static final int MIN_PIN_LENGTH = 4; 89 private static final int MAX_PIN_LENGTH = 8; 90 91 /** 92 * Delegate to the respective handlers. 93 */ 94 @Override 95 public void onPinEntered(EditPinPreference preference, boolean positiveResult) { 96 if (preference == mButtonEnableFDN) { 97 toggleFDNEnable(positiveResult); 98 } else if (preference == mButtonChangePin2){ 99 updatePINChangeState(positiveResult); 100 } 101 } 102 103 /** 104 * Attempt to toggle FDN activation. 105 */ 106 private void toggleFDNEnable(boolean positiveResult) { 107 if (!positiveResult) { 108 return; 109 } 110 111 // validate the pin first, before submitting it to the RIL for FDN enable. 112 String password = mButtonEnableFDN.getText(); 113 if (validatePin (password, false)) { 114 // get the relevant data for the icc call 115 boolean isEnabled = mPhone.getIccCard().getIccFdnEnabled(); 116 Message onComplete = mFDNHandler.obtainMessage(EVENT_PIN2_ENTRY_COMPLETE); 117 118 // make fdn request 119 mPhone.getIccCard().setIccFdnEnabled(!isEnabled, password, onComplete); 120 } else { 121 // throw up error if the pin is invalid. 122 displayMessage(R.string.invalidPin2); 123 } 124 125 mButtonEnableFDN.setText(""); 126 } 127 128 /** 129 * Attempt to change the pin. 130 */ 131 private void updatePINChangeState(boolean positiveResult) { 132 if (DBG) log("updatePINChangeState positive=" + positiveResult 133 + " mPinChangeState=" + mPinChangeState 134 + " mSkipOldPin=" + mIsPuk2Locked); 135 136 if (!positiveResult) { 137 // reset the state on cancel, either to expect PUK2 or PIN2 138 if (!mIsPuk2Locked) { 139 resetPinChangeState(); 140 } else { 141 resetPinChangeStateForPUK2(); 142 } 143 return; 144 } 145 146 // Progress through the dialog states, generally in this order: 147 // 1. Enter old pin 148 // 2. Enter new pin 149 // 3. Re-Enter new pin 150 // While handling any error conditions that may show up in between. 151 // Also handle the PUK2 entry, if it is requested. 152 // 153 // In general, if any invalid entries are made, the dialog re- 154 // appears with text to indicate what the issue is. 155 switch (mPinChangeState) { 156 case PIN_CHANGE_OLD: 157 mOldPin = mButtonChangePin2.getText(); 158 mButtonChangePin2.setText(""); 159 // if the pin is not valid, display a message and reset the state. 160 if (validatePin (mOldPin, false)) { 161 mPinChangeState = PIN_CHANGE_NEW; 162 displayPinChangeDialog(); 163 } else { 164 displayPinChangeDialog(R.string.invalidPin2, true); 165 } 166 break; 167 case PIN_CHANGE_NEW: 168 mNewPin = mButtonChangePin2.getText(); 169 mButtonChangePin2.setText(""); 170 // if the new pin is not valid, display a message and reset the state. 171 if (validatePin (mNewPin, false)) { 172 mPinChangeState = PIN_CHANGE_REENTER; 173 displayPinChangeDialog(); 174 } else { 175 displayPinChangeDialog(R.string.invalidPin2, true); 176 } 177 break; 178 case PIN_CHANGE_REENTER: 179 // if the re-entered pin is not valid, display a message and reset the state. 180 if (!mNewPin.equals(mButtonChangePin2.getText())) { 181 mPinChangeState = PIN_CHANGE_NEW; 182 mButtonChangePin2.setText(""); 183 displayPinChangeDialog(R.string.mismatchPin2, true); 184 } else { 185 // If the PIN is valid, then we submit the change PIN request. 186 mButtonChangePin2.setText(""); 187 Message onComplete = mFDNHandler.obtainMessage( 188 EVENT_PIN2_CHANGE_COMPLETE); 189 mPhone.getIccCard().changeIccFdnPassword( 190 mOldPin, mNewPin, onComplete); 191 } 192 break; 193 case PIN_CHANGE_PUK: { 194 // Doh! too many incorrect requests, PUK requested. 195 mPuk2 = mButtonChangePin2.getText(); 196 mButtonChangePin2.setText(""); 197 // if the puk is not valid, display 198 // a message and reset the state. 199 if (validatePin (mPuk2, true)) { 200 mPinChangeState = PIN_CHANGE_NEW_PIN_FOR_PUK; 201 displayPinChangeDialog(); 202 } else { 203 displayPinChangeDialog(R.string.invalidPuk2, true); 204 } 205 } 206 break; 207 case PIN_CHANGE_NEW_PIN_FOR_PUK: 208 mNewPin = mButtonChangePin2.getText(); 209 mButtonChangePin2.setText(""); 210 // if the new pin is not valid, display 211 // a message and reset the state. 212 if (validatePin (mNewPin, false)) { 213 mPinChangeState = PIN_CHANGE_REENTER_PIN_FOR_PUK; 214 displayPinChangeDialog(); 215 } else { 216 displayPinChangeDialog(R.string.invalidPin2, true); 217 } 218 break; 219 case PIN_CHANGE_REENTER_PIN_FOR_PUK: 220 // if the re-entered pin is not valid, display 221 // a message and reset the state. 222 if (!mNewPin.equals(mButtonChangePin2.getText())) { 223 mPinChangeState = PIN_CHANGE_NEW_PIN_FOR_PUK; 224 mButtonChangePin2.setText(""); 225 displayPinChangeDialog(R.string.mismatchPin2, true); 226 } else { 227 // Both puk2 and new pin2 are ready to submit 228 mButtonChangePin2.setText(""); 229 Message onComplete = mFDNHandler.obtainMessage( 230 EVENT_PIN2_CHANGE_COMPLETE); 231 mPhone.getIccCard().supplyPuk2(mPuk2, mNewPin, onComplete); 232 } 233 break; 234 } 235 } 236 237 /** 238 * Handler for asynchronous replies from the sim. 239 */ 240 private final Handler mFDNHandler = new Handler() { 241 @Override 242 public void handleMessage(Message msg) { 243 switch (msg.what) { 244 245 // when we are enabling FDN, either we are unsuccessful and display 246 // a toast, or just update the UI. 247 case EVENT_PIN2_ENTRY_COMPLETE: { 248 AsyncResult ar = (AsyncResult) msg.obj; 249 if (ar.exception != null && ar.exception instanceof CommandException) { 250 int attemptsRemaining = msg.arg1; 251 // see if PUK2 is requested and alert the user accordingly. 252 CommandException.Error e = 253 ((CommandException) ar.exception).getCommandError(); 254 switch (e) { 255 case SIM_PUK2: 256 // make sure we set the PUK2 state so that we can skip 257 // some redundant behaviour. 258 displayMessage(R.string.fdn_enable_puk2_requested, 259 attemptsRemaining); 260 resetPinChangeStateForPUK2(); 261 break; 262 case PASSWORD_INCORRECT: 263 displayMessage(R.string.pin2_invalid, attemptsRemaining); 264 break; 265 default: 266 displayMessage(R.string.fdn_failed, attemptsRemaining); 267 break; 268 } 269 } 270 updateEnableFDN(); 271 } 272 break; 273 274 // when changing the pin we need to pay attention to whether or not 275 // the error requests a PUK (usually after too many incorrect tries) 276 // Set the state accordingly. 277 case EVENT_PIN2_CHANGE_COMPLETE: { 278 if (DBG) 279 log("Handle EVENT_PIN2_CHANGE_COMPLETE"); 280 AsyncResult ar = (AsyncResult) msg.obj; 281 if (ar.exception != null) { 282 int attemptsRemaining = msg.arg1; 283 log("Handle EVENT_PIN2_CHANGE_COMPLETE attemptsRemaining=" 284 + attemptsRemaining); 285 CommandException ce = (CommandException) ar.exception; 286 if (ce.getCommandError() == CommandException.Error.SIM_PUK2) { 287 // throw an alert dialog on the screen, displaying the 288 // request for a PUK2. set the cancel listener to 289 // FdnSetting.onCancel(). 290 AlertDialog a = new AlertDialog.Builder(FdnSetting.this) 291 .setMessage(R.string.puk2_requested) 292 .setCancelable(true) 293 .setOnCancelListener(FdnSetting.this) 294 .create(); 295 a.getWindow().addFlags( 296 WindowManager.LayoutParams.FLAG_DIM_BEHIND); 297 a.show(); 298 } else { 299 // set the correct error message depending upon the state. 300 // Reset the state depending upon or knowledge of the PUK state. 301 if (!mIsPuk2Locked) { 302 displayMessage(R.string.badPin2, attemptsRemaining); 303 resetPinChangeState(); 304 } else { 305 displayMessage(R.string.badPuk2, attemptsRemaining); 306 resetPinChangeStateForPUK2(); 307 } 308 } 309 } else { 310 if (mPinChangeState == PIN_CHANGE_PUK) { 311 displayMessage(R.string.pin2_unblocked); 312 } else { 313 displayMessage(R.string.pin2_changed); 314 } 315 316 // reset to normal behaviour on successful change. 317 resetPinChangeState(); 318 } 319 } 320 break; 321 } 322 } 323 }; 324 325 /** 326 * Cancel listener for the PUK2 request alert dialog. 327 */ 328 @Override 329 public void onCancel(DialogInterface dialog) { 330 // set the state of the preference and then display the dialog. 331 resetPinChangeStateForPUK2(); 332 displayPinChangeDialog(0, true); 333 } 334 335 /** 336 * Display a toast for message, like the rest of the settings. 337 */ 338 private final void displayMessage(int strId, int attemptsRemaining) { 339 String s = getString(strId); 340 if ((strId == R.string.badPin2) || (strId == R.string.badPuk2) || 341 (strId == R.string.pin2_invalid)) { 342 if (attemptsRemaining >= 0) { 343 s = getString(strId) + getString(R.string.pin2_attempts, attemptsRemaining); 344 } else { 345 s = getString(strId); 346 } 347 } 348 log("displayMessage: attemptsRemaining=" + attemptsRemaining + " s=" + s); 349 Toast.makeText(this, s, Toast.LENGTH_SHORT).show(); 350 } 351 352 private final void displayMessage(int strId) { 353 displayMessage(strId, -1); 354 } 355 356 /** 357 * The next two functions are for updating the message field on the dialog. 358 */ 359 private final void displayPinChangeDialog() { 360 displayPinChangeDialog(0, true); 361 } 362 363 private final void displayPinChangeDialog(int strId, boolean shouldDisplay) { 364 int msgId; 365 switch (mPinChangeState) { 366 case PIN_CHANGE_OLD: 367 msgId = R.string.oldPin2Label; 368 break; 369 case PIN_CHANGE_NEW: 370 case PIN_CHANGE_NEW_PIN_FOR_PUK: 371 msgId = R.string.newPin2Label; 372 break; 373 case PIN_CHANGE_REENTER: 374 case PIN_CHANGE_REENTER_PIN_FOR_PUK: 375 msgId = R.string.confirmPin2Label; 376 break; 377 case PIN_CHANGE_PUK: 378 default: 379 msgId = R.string.label_puk2_code; 380 break; 381 } 382 383 // append the note / additional message, if needed. 384 if (strId != 0) { 385 mButtonChangePin2.setDialogMessage(getText(msgId) + "\n" + getText(strId)); 386 } else { 387 mButtonChangePin2.setDialogMessage(msgId); 388 } 389 390 // only display if requested. 391 if (shouldDisplay) { 392 mButtonChangePin2.showPinDialog(); 393 } 394 } 395 396 /** 397 * Reset the state of the pin change dialog. 398 */ 399 private final void resetPinChangeState() { 400 if (DBG) log("resetPinChangeState"); 401 mPinChangeState = PIN_CHANGE_OLD; 402 displayPinChangeDialog(0, false); 403 mOldPin = mNewPin = ""; 404 mIsPuk2Locked = false; 405 } 406 407 /** 408 * Reset the state of the pin change dialog solely for PUK2 use. 409 */ 410 private final void resetPinChangeStateForPUK2() { 411 if (DBG) log("resetPinChangeStateForPUK2"); 412 mPinChangeState = PIN_CHANGE_PUK; 413 displayPinChangeDialog(0, false); 414 mOldPin = mNewPin = mPuk2 = ""; 415 mIsPuk2Locked = true; 416 } 417 418 /** 419 * Validate the pin entry. 420 * 421 * @param pin This is the pin to validate 422 * @param isPuk Boolean indicating whether we are to treat 423 * the pin input as a puk. 424 */ 425 private boolean validatePin(String pin, boolean isPuk) { 426 427 // for pin, we have 4-8 numbers, or puk, we use only 8. 428 int pinMinimum = isPuk ? MAX_PIN_LENGTH : MIN_PIN_LENGTH; 429 430 // check validity 431 if (pin == null || pin.length() < pinMinimum || pin.length() > MAX_PIN_LENGTH) { 432 return false; 433 } else { 434 return true; 435 } 436 } 437 438 /** 439 * Reflect the updated FDN state in the UI. 440 */ 441 private void updateEnableFDN() { 442 if (mPhone.getIccCard().getIccFdnEnabled()) { 443 mButtonEnableFDN.setTitle(R.string.enable_fdn_ok); 444 mButtonEnableFDN.setSummary(R.string.fdn_enabled); 445 mButtonEnableFDN.setDialogTitle(R.string.disable_fdn); 446 } else { 447 mButtonEnableFDN.setTitle(R.string.disable_fdn_ok); 448 mButtonEnableFDN.setSummary(R.string.fdn_disabled); 449 mButtonEnableFDN.setDialogTitle(R.string.enable_fdn); 450 } 451 } 452 453 @Override 454 protected void onCreate(Bundle icicle) { 455 super.onCreate(icicle); 456 457 addPreferencesFromResource(R.xml.fdn_setting); 458 459 mPhone = PhoneGlobals.getPhone(); 460 461 //get UI object references 462 PreferenceScreen prefSet = getPreferenceScreen(); 463 mButtonEnableFDN = (EditPinPreference) prefSet.findPreference(BUTTON_FDN_ENABLE_KEY); 464 mButtonChangePin2 = (EditPinPreference) prefSet.findPreference(BUTTON_CHANGE_PIN2_KEY); 465 466 //assign click listener and update state 467 mButtonEnableFDN.setOnPinEnteredListener(this); 468 updateEnableFDN(); 469 470 mButtonChangePin2.setOnPinEnteredListener(this); 471 472 // Only reset the pin change dialog if we're not in the middle of changing it. 473 if (icicle == null) { 474 resetPinChangeState(); 475 } else { 476 mIsPuk2Locked = icicle.getBoolean(SKIP_OLD_PIN_KEY); 477 mPinChangeState = icicle.getInt(PIN_CHANGE_STATE_KEY); 478 mOldPin = icicle.getString(OLD_PIN_KEY); 479 mNewPin = icicle.getString(NEW_PIN_KEY); 480 mButtonChangePin2.setDialogMessage(icicle.getString(DIALOG_MESSAGE_KEY)); 481 mButtonChangePin2.setText(icicle.getString(DIALOG_PIN_ENTRY_KEY)); 482 } 483 484 ActionBar actionBar = getActionBar(); 485 if (actionBar != null) { 486 // android.R.id.home will be triggered in onOptionsItemSelected() 487 actionBar.setDisplayHomeAsUpEnabled(true); 488 } 489 } 490 491 @Override 492 protected void onResume() { 493 super.onResume(); 494 mPhone = PhoneGlobals.getPhone(); 495 updateEnableFDN(); 496 } 497 498 /** 499 * Save the state of the pin change. 500 */ 501 @Override 502 protected void onSaveInstanceState(Bundle out) { 503 super.onSaveInstanceState(out); 504 out.putBoolean(SKIP_OLD_PIN_KEY, mIsPuk2Locked); 505 out.putInt(PIN_CHANGE_STATE_KEY, mPinChangeState); 506 out.putString(OLD_PIN_KEY, mOldPin); 507 out.putString(NEW_PIN_KEY, mNewPin); 508 out.putString(DIALOG_MESSAGE_KEY, mButtonChangePin2.getDialogMessage().toString()); 509 out.putString(DIALOG_PIN_ENTRY_KEY, mButtonChangePin2.getText()); 510 } 511 512 @Override 513 public boolean onOptionsItemSelected(MenuItem item) { 514 final int itemId = item.getItemId(); 515 if (itemId == android.R.id.home) { // See ActionBar#setDisplayHomeAsUpEnabled() 516 CallFeaturesSetting.goUpToTopLevelSetting(this); 517 return true; 518 } 519 return super.onOptionsItemSelected(item); 520 } 521 522 private void log(String msg) { 523 Log.d(LOG_TAG, "FdnSetting: " + msg); 524 } 525} 526 527