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