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