Home | History | Annotate | Download | only in settings
      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.settings;
     18 
     19 import android.content.Context;
     20 import android.content.res.Resources;
     21 import android.os.AsyncResult;
     22 import android.os.Bundle;
     23 import android.os.Handler;
     24 import android.os.Message;
     25 import android.preference.CheckBoxPreference;
     26 import android.preference.Preference;
     27 import android.preference.PreferenceActivity;
     28 import android.preference.PreferenceScreen;
     29 import android.widget.Toast;
     30 
     31 import com.android.internal.telephony.Phone;
     32 import com.android.internal.telephony.PhoneFactory;
     33 
     34 /**
     35  * Implements the preference screen to enable/disable ICC lock and
     36  * also the dialogs to change the ICC PIN. In the former case, enabling/disabling
     37  * the ICC lock will prompt the user for the current PIN.
     38  * In the Change PIN case, it prompts the user for old pin, new pin and new pin
     39  * again before attempting to change it. Calls the SimCard interface to execute
     40  * these operations.
     41  *
     42  */
     43 public class IccLockSettings extends PreferenceActivity
     44         implements EditPinPreference.OnPinEnteredListener {
     45 
     46     private static final int OFF_MODE = 0;
     47     // State when enabling/disabling ICC lock
     48     private static final int ICC_LOCK_MODE = 1;
     49     // State when entering the old pin
     50     private static final int ICC_OLD_MODE = 2;
     51     // State when entering the new pin - first time
     52     private static final int ICC_NEW_MODE = 3;
     53     // State when entering the new pin - second time
     54     private static final int ICC_REENTER_MODE = 4;
     55 
     56     // Keys in xml file
     57     private static final String PIN_DIALOG = "sim_pin";
     58     private static final String PIN_TOGGLE = "sim_toggle";
     59     // Keys in icicle
     60     private static final String DIALOG_STATE = "dialogState";
     61     private static final String DIALOG_PIN = "dialogPin";
     62     private static final String DIALOG_ERROR = "dialogError";
     63     private static final String ENABLE_TO_STATE = "enableState";
     64 
     65     private static final int MIN_PIN_LENGTH = 4;
     66     private static final int MAX_PIN_LENGTH = 8;
     67     // Which dialog to show next when popped up
     68     private int mDialogState = OFF_MODE;
     69 
     70     private String mPin;
     71     private String mOldPin;
     72     private String mNewPin;
     73     private String mError;
     74     // Are we trying to enable or disable ICC lock?
     75     private boolean mToState;
     76 
     77     private Phone mPhone;
     78 
     79     private EditPinPreference mPinDialog;
     80     private CheckBoxPreference mPinToggle;
     81 
     82     private Resources mRes;
     83 
     84     // For async handler to identify request type
     85     private static final int ENABLE_ICC_PIN_COMPLETE = 100;
     86     private static final int CHANGE_ICC_PIN_COMPLETE = 101;
     87 
     88     // For replies from IccCard interface
     89     private Handler mHandler = new Handler() {
     90         public void handleMessage(Message msg) {
     91             AsyncResult ar = (AsyncResult) msg.obj;
     92             switch (msg.what) {
     93                 case ENABLE_ICC_PIN_COMPLETE:
     94                     iccLockChanged(ar.exception == null);
     95                     break;
     96                 case CHANGE_ICC_PIN_COMPLETE:
     97                     iccPinChanged(ar.exception == null);
     98                     break;
     99             }
    100 
    101             return;
    102         }
    103     };
    104 
    105     // For top-level settings screen to query
    106     static boolean isIccLockEnabled() {
    107         return PhoneFactory.getDefaultPhone().getIccCard().getIccLockEnabled();
    108     }
    109 
    110     static String getSummary(Context context) {
    111         Resources res = context.getResources();
    112         String summary = isIccLockEnabled()
    113                 ? res.getString(R.string.sim_lock_on)
    114                 : res.getString(R.string.sim_lock_off);
    115         return summary;
    116     }
    117 
    118     @Override
    119     protected void onCreate(Bundle savedInstanceState) {
    120         super.onCreate(savedInstanceState);
    121 
    122         if (Utils.isMonkeyRunning()) {
    123             finish();
    124             return;
    125         }
    126 
    127         addPreferencesFromResource(R.xml.sim_lock_settings);
    128 
    129         mPinDialog = (EditPinPreference) findPreference(PIN_DIALOG);
    130         mPinToggle = (CheckBoxPreference) findPreference(PIN_TOGGLE);
    131         if (savedInstanceState != null && savedInstanceState.containsKey(DIALOG_STATE)) {
    132             mDialogState = savedInstanceState.getInt(DIALOG_STATE);
    133             mPin = savedInstanceState.getString(DIALOG_PIN);
    134             mError = savedInstanceState.getString(DIALOG_ERROR);
    135             mToState = savedInstanceState.getBoolean(ENABLE_TO_STATE);
    136         }
    137 
    138         mPinDialog.setOnPinEnteredListener(this);
    139 
    140         // Don't need any changes to be remembered
    141         getPreferenceScreen().setPersistent(false);
    142 
    143         mPhone = PhoneFactory.getDefaultPhone();
    144         mRes = getResources();
    145     }
    146 
    147     @Override
    148     protected void onResume() {
    149         super.onResume();
    150 
    151         mPinToggle.setChecked(mPhone.getIccCard().getIccLockEnabled());
    152 
    153         if (mDialogState != OFF_MODE) {
    154             showPinDialog();
    155         } else {
    156             // Prep for standard click on "Change PIN"
    157             resetDialogState();
    158         }
    159     }
    160 
    161     @Override
    162     protected void onSaveInstanceState(Bundle out) {
    163         // Need to store this state for slider open/close
    164         // There is one case where the dialog is popped up by the preference
    165         // framework. In that case, let the preference framework store the
    166         // dialog state. In other cases, where this activity manually launches
    167         // the dialog, store the state of the dialog.
    168         if (mPinDialog.isDialogOpen()) {
    169             out.putInt(DIALOG_STATE, mDialogState);
    170             out.putString(DIALOG_PIN, mPinDialog.getEditText().getText().toString());
    171             out.putString(DIALOG_ERROR, mError);
    172             out.putBoolean(ENABLE_TO_STATE, mToState);
    173         } else {
    174             super.onSaveInstanceState(out);
    175         }
    176     }
    177 
    178     private void showPinDialog() {
    179         if (mDialogState == OFF_MODE) {
    180             return;
    181         }
    182         setDialogValues();
    183 
    184         mPinDialog.showPinDialog();
    185     }
    186 
    187     private void setDialogValues() {
    188         mPinDialog.setText(mPin);
    189         String message = "";
    190         switch (mDialogState) {
    191             case ICC_LOCK_MODE:
    192                 message = mRes.getString(R.string.sim_enter_pin);
    193                 mPinDialog.setDialogTitle(mToState
    194                         ? mRes.getString(R.string.sim_enable_sim_lock)
    195                         : mRes.getString(R.string.sim_disable_sim_lock));
    196                 break;
    197             case ICC_OLD_MODE:
    198                 message = mRes.getString(R.string.sim_enter_old);
    199                 mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin));
    200                 break;
    201             case ICC_NEW_MODE:
    202                 message = mRes.getString(R.string.sim_enter_new);
    203                 mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin));
    204                 break;
    205             case ICC_REENTER_MODE:
    206                 message = mRes.getString(R.string.sim_reenter_new);
    207                 mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin));
    208                 break;
    209         }
    210         if (mError != null) {
    211             message = mError + "\n" + message;
    212             mError = null;
    213         }
    214         mPinDialog.setDialogMessage(message);
    215     }
    216 
    217     public void onPinEntered(EditPinPreference preference, boolean positiveResult) {
    218         if (!positiveResult) {
    219             resetDialogState();
    220             return;
    221         }
    222 
    223         mPin = preference.getText();
    224         if (!reasonablePin(mPin)) {
    225             // inject error message and display dialog again
    226             mError = mRes.getString(R.string.sim_bad_pin);
    227             showPinDialog();
    228             return;
    229         }
    230         switch (mDialogState) {
    231             case ICC_LOCK_MODE:
    232                 tryChangeIccLockState();
    233                 break;
    234             case ICC_OLD_MODE:
    235                 mOldPin = mPin;
    236                 mDialogState = ICC_NEW_MODE;
    237                 mError = null;
    238                 mPin = null;
    239                 showPinDialog();
    240                 break;
    241             case ICC_NEW_MODE:
    242                 mNewPin = mPin;
    243                 mDialogState = ICC_REENTER_MODE;
    244                 mPin = null;
    245                 showPinDialog();
    246                 break;
    247             case ICC_REENTER_MODE:
    248                 if (!mPin.equals(mNewPin)) {
    249                     mError = mRes.getString(R.string.sim_pins_dont_match);
    250                     mDialogState = ICC_NEW_MODE;
    251                     mPin = null;
    252                     showPinDialog();
    253                 } else {
    254                     mError = null;
    255                     tryChangePin();
    256                 }
    257                 break;
    258         }
    259     }
    260 
    261     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
    262         if (preference == mPinToggle) {
    263             // Get the new, preferred state
    264             mToState = mPinToggle.isChecked();
    265             // Flip it back and pop up pin dialog
    266             mPinToggle.setChecked(!mToState);
    267             mDialogState = ICC_LOCK_MODE;
    268             showPinDialog();
    269         } else if (preference == mPinDialog) {
    270             mDialogState = ICC_OLD_MODE;
    271             return false;
    272         }
    273         return true;
    274     }
    275 
    276     private void tryChangeIccLockState() {
    277         // Try to change icc lock. If it succeeds, toggle the lock state and
    278         // reset dialog state. Else inject error message and show dialog again.
    279         Message callback = Message.obtain(mHandler, ENABLE_ICC_PIN_COMPLETE);
    280         mPhone.getIccCard().setIccLockEnabled(mToState, mPin, callback);
    281 
    282     }
    283 
    284     private void iccLockChanged(boolean success) {
    285         if (success) {
    286             mPinToggle.setChecked(mToState);
    287         } else {
    288             Toast.makeText(this, mRes.getString(R.string.sim_lock_failed), Toast.LENGTH_SHORT)
    289                     .show();
    290         }
    291         resetDialogState();
    292     }
    293 
    294     private void iccPinChanged(boolean success) {
    295         if (!success) {
    296             Toast.makeText(this, mRes.getString(R.string.sim_change_failed),
    297                     Toast.LENGTH_SHORT)
    298                     .show();
    299         } else {
    300             Toast.makeText(this, mRes.getString(R.string.sim_change_succeeded),
    301                     Toast.LENGTH_SHORT)
    302                     .show();
    303 
    304         }
    305         resetDialogState();
    306     }
    307 
    308     private void tryChangePin() {
    309         Message callback = Message.obtain(mHandler, CHANGE_ICC_PIN_COMPLETE);
    310         mPhone.getIccCard().changeIccLockPassword(mOldPin,
    311                 mNewPin, callback);
    312     }
    313 
    314     private boolean reasonablePin(String pin) {
    315         if (pin == null || pin.length() < MIN_PIN_LENGTH || pin.length() > MAX_PIN_LENGTH) {
    316             return false;
    317         } else {
    318             return true;
    319         }
    320     }
    321 
    322     private void resetDialogState() {
    323         mError = null;
    324         mDialogState = ICC_OLD_MODE; // Default for when Change PIN is clicked
    325         mPin = "";
    326         setDialogValues();
    327         mDialogState = OFF_MODE;
    328     }
    329 }
    330