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