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 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