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 && ar.exception instanceof CommandException) {
    247                             int attemptsRemaining = msg.arg1;
    248                             // see if PUK2 is requested and alert the user accordingly.
    249                             CommandException.Error e =
    250                                     ((CommandException) ar.exception).getCommandError();
    251                             switch (e) {
    252                                 case SIM_PUK2:
    253                                     // make sure we set the PUK2 state so that we can skip
    254                                     // some redundant behaviour.
    255                                     displayMessage(R.string.fdn_enable_puk2_requested,
    256                                             attemptsRemaining);
    257                                     resetPinChangeStateForPUK2();
    258                                     break;
    259                                 case PASSWORD_INCORRECT:
    260                                     displayMessage(R.string.pin2_invalid, attemptsRemaining);
    261                                     break;
    262                                 default:
    263                                     displayMessage(R.string.fdn_failed, attemptsRemaining);
    264                                     break;
    265                             }
    266                         }
    267                         updateEnableFDN();
    268                     }
    269                     break;
    270 
    271                 // when changing the pin we need to pay attention to whether or not
    272                 // the error requests a PUK (usually after too many incorrect tries)
    273                 // Set the state accordingly.
    274                 case EVENT_PIN2_CHANGE_COMPLETE: {
    275                         if (DBG)
    276                             log("Handle EVENT_PIN2_CHANGE_COMPLETE");
    277                         AsyncResult ar = (AsyncResult) msg.obj;
    278                         if (ar.exception != null) {
    279                             int attemptsRemaining = msg.arg1;
    280                             log("Handle EVENT_PIN2_CHANGE_COMPLETE attemptsRemaining="
    281                                     + attemptsRemaining);
    282                             CommandException ce = (CommandException) ar.exception;
    283                             if (ce.getCommandError() == CommandException.Error.SIM_PUK2) {
    284                                 // throw an alert dialog on the screen, displaying the
    285                                 // request for a PUK2.  set the cancel listener to
    286                                 // FdnSetting.onCancel().
    287                                 AlertDialog a = new AlertDialog.Builder(FdnSetting.this)
    288                                     .setMessage(R.string.puk2_requested)
    289                                     .setCancelable(true)
    290                                     .setOnCancelListener(FdnSetting.this)
    291                                     .create();
    292                                 a.getWindow().addFlags(
    293                                         WindowManager.LayoutParams.FLAG_DIM_BEHIND);
    294                                 a.show();
    295                             } else {
    296                                 // set the correct error message depending upon the state.
    297                                 // Reset the state depending upon or knowledge of the PUK state.
    298                                 if (!mIsPuk2Locked) {
    299                                     displayMessage(R.string.badPin2, attemptsRemaining);
    300                                     resetPinChangeState();
    301                                 } else {
    302                                     displayMessage(R.string.badPuk2, attemptsRemaining);
    303                                     resetPinChangeStateForPUK2();
    304                                 }
    305                             }
    306                         } else {
    307                             if (mPinChangeState == PIN_CHANGE_PUK) {
    308                                 displayMessage(R.string.pin2_unblocked);
    309                             } else {
    310                                 displayMessage(R.string.pin2_changed);
    311                             }
    312 
    313                             // reset to normal behaviour on successful change.
    314                             resetPinChangeState();
    315                         }
    316                     }
    317                     break;
    318             }
    319         }
    320     };
    321 
    322     /**
    323      * Cancel listener for the PUK2 request alert dialog.
    324      */
    325     @Override
    326     public void onCancel(DialogInterface dialog) {
    327         // set the state of the preference and then display the dialog.
    328         resetPinChangeStateForPUK2();
    329         displayPinChangeDialog(0, true);
    330     }
    331 
    332     /**
    333      * Display a toast for message, like the rest of the settings.
    334      */
    335     private final void displayMessage(int strId, int attemptsRemaining) {
    336         String s = getString(strId);
    337         if ((strId == R.string.badPin2) || (strId == R.string.badPuk2) ||
    338                 (strId == R.string.pin2_invalid)) {
    339             if (attemptsRemaining >= 0) {
    340                 s = getString(strId) + getString(R.string.pin2_attempts, attemptsRemaining);
    341             } else {
    342                 s = getString(strId);
    343             }
    344         }
    345         log("displayMessage: attemptsRemaining=" + attemptsRemaining + " s=" + s);
    346         Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
    347     }
    348 
    349     private final void displayMessage(int strId) {
    350         displayMessage(strId, -1);
    351     }
    352 
    353     /**
    354      * The next two functions are for updating the message field on the dialog.
    355      */
    356     private final void displayPinChangeDialog() {
    357         displayPinChangeDialog(0, true);
    358     }
    359 
    360     private final void displayPinChangeDialog(int strId, boolean shouldDisplay) {
    361         int msgId;
    362         switch (mPinChangeState) {
    363             case PIN_CHANGE_OLD:
    364                 msgId = R.string.oldPin2Label;
    365                 break;
    366             case PIN_CHANGE_NEW:
    367             case PIN_CHANGE_NEW_PIN_FOR_PUK:
    368                 msgId = R.string.newPin2Label;
    369                 break;
    370             case PIN_CHANGE_REENTER:
    371             case PIN_CHANGE_REENTER_PIN_FOR_PUK:
    372                 msgId = R.string.confirmPin2Label;
    373                 break;
    374             case PIN_CHANGE_PUK:
    375             default:
    376                 msgId = R.string.label_puk2_code;
    377                 break;
    378         }
    379 
    380         // append the note / additional message, if needed.
    381         if (strId != 0) {
    382             mButtonChangePin2.setDialogMessage(getText(msgId) + "\n" + getText(strId));
    383         } else {
    384             mButtonChangePin2.setDialogMessage(msgId);
    385         }
    386 
    387         // only display if requested.
    388         if (shouldDisplay) {
    389             mButtonChangePin2.showPinDialog();
    390         }
    391     }
    392 
    393     /**
    394      * Reset the state of the pin change dialog.
    395      */
    396     private final void resetPinChangeState() {
    397         if (DBG) log("resetPinChangeState");
    398         mPinChangeState = PIN_CHANGE_OLD;
    399         displayPinChangeDialog(0, false);
    400         mOldPin = mNewPin = "";
    401         mIsPuk2Locked = false;
    402     }
    403 
    404     /**
    405      * Reset the state of the pin change dialog solely for PUK2 use.
    406      */
    407     private final void resetPinChangeStateForPUK2() {
    408         if (DBG) log("resetPinChangeStateForPUK2");
    409         mPinChangeState = PIN_CHANGE_PUK;
    410         displayPinChangeDialog(0, false);
    411         mOldPin = mNewPin = mPuk2 = "";
    412         mIsPuk2Locked = true;
    413     }
    414 
    415     /**
    416      * Validate the pin entry.
    417      *
    418      * @param pin This is the pin to validate
    419      * @param isPuk Boolean indicating whether we are to treat
    420      * the pin input as a puk.
    421      */
    422     private boolean validatePin(String pin, boolean isPuk) {
    423 
    424         // for pin, we have 4-8 numbers, or puk, we use only 8.
    425         int pinMinimum = isPuk ? MAX_PIN_LENGTH : MIN_PIN_LENGTH;
    426 
    427         // check validity
    428         if (pin == null || pin.length() < pinMinimum || pin.length() > MAX_PIN_LENGTH) {
    429             return false;
    430         } else {
    431             return true;
    432         }
    433     }
    434 
    435     /**
    436      * Reflect the updated FDN state in the UI.
    437      */
    438     private void updateEnableFDN() {
    439         if (mPhone.getIccCard().getIccFdnEnabled()) {
    440             mButtonEnableFDN.setTitle(R.string.enable_fdn_ok);
    441             mButtonEnableFDN.setSummary(R.string.fdn_enabled);
    442             mButtonEnableFDN.setDialogTitle(R.string.disable_fdn);
    443         } else {
    444             mButtonEnableFDN.setTitle(R.string.disable_fdn_ok);
    445             mButtonEnableFDN.setSummary(R.string.fdn_disabled);
    446             mButtonEnableFDN.setDialogTitle(R.string.enable_fdn);
    447         }
    448     }
    449 
    450     @Override
    451     protected void onCreate(Bundle icicle) {
    452         super.onCreate(icicle);
    453 
    454         addPreferencesFromResource(R.xml.fdn_setting);
    455 
    456         mPhone = PhoneGlobals.getPhone();
    457 
    458         //get UI object references
    459         PreferenceScreen prefSet = getPreferenceScreen();
    460         mButtonEnableFDN = (EditPinPreference) prefSet.findPreference(BUTTON_FDN_ENABLE_KEY);
    461         mButtonChangePin2 = (EditPinPreference) prefSet.findPreference(BUTTON_CHANGE_PIN2_KEY);
    462 
    463         //assign click listener and update state
    464         mButtonEnableFDN.setOnPinEnteredListener(this);
    465         updateEnableFDN();
    466 
    467         mButtonChangePin2.setOnPinEnteredListener(this);
    468 
    469         // Only reset the pin change dialog if we're not in the middle of changing it.
    470         if (icicle == null) {
    471             resetPinChangeState();
    472         } else {
    473             mIsPuk2Locked = icicle.getBoolean(SKIP_OLD_PIN_KEY);
    474             mPinChangeState = icicle.getInt(PIN_CHANGE_STATE_KEY);
    475             mOldPin = icicle.getString(OLD_PIN_KEY);
    476             mNewPin = icicle.getString(NEW_PIN_KEY);
    477             mButtonChangePin2.setDialogMessage(icicle.getString(DIALOG_MESSAGE_KEY));
    478             mButtonChangePin2.setText(icicle.getString(DIALOG_PIN_ENTRY_KEY));
    479         }
    480 
    481         ActionBar actionBar = getActionBar();
    482         if (actionBar != null) {
    483             // android.R.id.home will be triggered in onOptionsItemSelected()
    484             actionBar.setDisplayHomeAsUpEnabled(true);
    485         }
    486     }
    487 
    488     @Override
    489     protected void onResume() {
    490         super.onResume();
    491         mPhone = PhoneGlobals.getPhone();
    492         updateEnableFDN();
    493     }
    494 
    495     /**
    496      * Save the state of the pin change.
    497      */
    498     @Override
    499     protected void onSaveInstanceState(Bundle out) {
    500         super.onSaveInstanceState(out);
    501         out.putBoolean(SKIP_OLD_PIN_KEY, mIsPuk2Locked);
    502         out.putInt(PIN_CHANGE_STATE_KEY, mPinChangeState);
    503         out.putString(OLD_PIN_KEY, mOldPin);
    504         out.putString(NEW_PIN_KEY, mNewPin);
    505         out.putString(DIALOG_MESSAGE_KEY, mButtonChangePin2.getDialogMessage().toString());
    506         out.putString(DIALOG_PIN_ENTRY_KEY, mButtonChangePin2.getText());
    507     }
    508 
    509     @Override
    510     public boolean onOptionsItemSelected(MenuItem item) {
    511         final int itemId = item.getItemId();
    512         if (itemId == android.R.id.home) {  // See ActionBar#setDisplayHomeAsUpEnabled()
    513             CallFeaturesSetting.goUpToTopLevelSetting(this);
    514             return true;
    515         }
    516         return super.onOptionsItemSelected(item);
    517     }
    518 
    519     private void log(String msg) {
    520         Log.d(LOG_TAG, "FdnSetting: " + msg);
    521     }
    522 }
    523 
    524