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