Home | History | Annotate | Download | only in phone
      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