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