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