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 // Save and restore inputted PIN code when configuration changed 66 // (ex. portrait<-->landscape) during change PIN code 67 private static final String OLD_PINCODE = "oldPinCode"; 68 private static final String NEW_PINCODE = "newPinCode"; 69 70 private static final int MIN_PIN_LENGTH = 4; 71 private static final int MAX_PIN_LENGTH = 8; 72 // Which dialog to show next when popped up 73 private int mDialogState = OFF_MODE; 74 75 private String mPin; 76 private String mOldPin; 77 private String mNewPin; 78 private String mError; 79 // Are we trying to enable or disable ICC lock? 80 private boolean mToState; 81 82 private Phone mPhone; 83 84 private EditPinPreference mPinDialog; 85 private CheckBoxPreference mPinToggle; 86 87 private Resources mRes; 88 89 // For async handler to identify request type 90 private static final int ENABLE_ICC_PIN_COMPLETE = 100; 91 private static final int CHANGE_ICC_PIN_COMPLETE = 101; 92 93 // For replies from IccCard interface 94 private Handler mHandler = new Handler() { 95 public void handleMessage(Message msg) { 96 AsyncResult ar = (AsyncResult) msg.obj; 97 switch (msg.what) { 98 case ENABLE_ICC_PIN_COMPLETE: 99 iccLockChanged(ar.exception == null); 100 break; 101 case CHANGE_ICC_PIN_COMPLETE: 102 iccPinChanged(ar.exception == null); 103 break; 104 } 105 106 return; 107 } 108 }; 109 110 // For top-level settings screen to query isIccLockEnabled()111 static boolean isIccLockEnabled() { 112 return PhoneFactory.getDefaultPhone().getIccCard().getIccLockEnabled(); 113 } 114 getSummary(Context context)115 static String getSummary(Context context) { 116 Resources res = context.getResources(); 117 String summary = isIccLockEnabled() 118 ? res.getString(R.string.sim_lock_on) 119 : res.getString(R.string.sim_lock_off); 120 return summary; 121 } 122 123 @Override onCreate(Bundle savedInstanceState)124 protected void onCreate(Bundle savedInstanceState) { 125 super.onCreate(savedInstanceState); 126 127 if (Utils.isMonkeyRunning()) { 128 finish(); 129 return; 130 } 131 132 addPreferencesFromResource(R.xml.sim_lock_settings); 133 134 mPinDialog = (EditPinPreference) findPreference(PIN_DIALOG); 135 mPinToggle = (CheckBoxPreference) findPreference(PIN_TOGGLE); 136 if (savedInstanceState != null && savedInstanceState.containsKey(DIALOG_STATE)) { 137 mDialogState = savedInstanceState.getInt(DIALOG_STATE); 138 mPin = savedInstanceState.getString(DIALOG_PIN); 139 mError = savedInstanceState.getString(DIALOG_ERROR); 140 mToState = savedInstanceState.getBoolean(ENABLE_TO_STATE); 141 142 // Restore inputted PIN code 143 switch (mDialogState) { 144 case ICC_NEW_MODE: 145 mOldPin = savedInstanceState.getString(OLD_PINCODE); 146 break; 147 148 case ICC_REENTER_MODE: 149 mOldPin = savedInstanceState.getString(OLD_PINCODE); 150 mNewPin = savedInstanceState.getString(NEW_PINCODE); 151 break; 152 153 case ICC_LOCK_MODE: 154 case ICC_OLD_MODE: 155 default: 156 break; 157 } 158 } 159 160 mPinDialog.setOnPinEnteredListener(this); 161 162 // Don't need any changes to be remembered 163 getPreferenceScreen().setPersistent(false); 164 165 mPhone = PhoneFactory.getDefaultPhone(); 166 mRes = getResources(); 167 } 168 169 @Override onResume()170 protected void onResume() { 171 super.onResume(); 172 173 mPinToggle.setChecked(mPhone.getIccCard().getIccLockEnabled()); 174 175 if (mDialogState != OFF_MODE) { 176 showPinDialog(); 177 } else { 178 // Prep for standard click on "Change PIN" 179 resetDialogState(); 180 } 181 } 182 183 @Override onSaveInstanceState(Bundle out)184 protected void onSaveInstanceState(Bundle out) { 185 // Need to store this state for slider open/close 186 // There is one case where the dialog is popped up by the preference 187 // framework. In that case, let the preference framework store the 188 // dialog state. In other cases, where this activity manually launches 189 // the dialog, store the state of the dialog. 190 if (mPinDialog.isDialogOpen()) { 191 out.putInt(DIALOG_STATE, mDialogState); 192 out.putString(DIALOG_PIN, mPinDialog.getEditText().getText().toString()); 193 out.putString(DIALOG_ERROR, mError); 194 out.putBoolean(ENABLE_TO_STATE, mToState); 195 196 // Save inputted PIN code 197 switch (mDialogState) { 198 case ICC_NEW_MODE: 199 out.putString(OLD_PINCODE, mOldPin); 200 break; 201 202 case ICC_REENTER_MODE: 203 out.putString(OLD_PINCODE, mOldPin); 204 out.putString(NEW_PINCODE, mNewPin); 205 break; 206 207 case ICC_LOCK_MODE: 208 case ICC_OLD_MODE: 209 default: 210 break; 211 } 212 } else { 213 super.onSaveInstanceState(out); 214 } 215 } 216 showPinDialog()217 private void showPinDialog() { 218 if (mDialogState == OFF_MODE) { 219 return; 220 } 221 setDialogValues(); 222 223 mPinDialog.showPinDialog(); 224 } 225 setDialogValues()226 private void setDialogValues() { 227 mPinDialog.setText(mPin); 228 String message = ""; 229 switch (mDialogState) { 230 case ICC_LOCK_MODE: 231 message = mRes.getString(R.string.sim_enter_pin); 232 mPinDialog.setDialogTitle(mToState 233 ? mRes.getString(R.string.sim_enable_sim_lock) 234 : mRes.getString(R.string.sim_disable_sim_lock)); 235 break; 236 case ICC_OLD_MODE: 237 message = mRes.getString(R.string.sim_enter_old); 238 mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin)); 239 break; 240 case ICC_NEW_MODE: 241 message = mRes.getString(R.string.sim_enter_new); 242 mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin)); 243 break; 244 case ICC_REENTER_MODE: 245 message = mRes.getString(R.string.sim_reenter_new); 246 mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin)); 247 break; 248 } 249 if (mError != null) { 250 message = mError + "\n" + message; 251 mError = null; 252 } 253 mPinDialog.setDialogMessage(message); 254 } 255 onPinEntered(EditPinPreference preference, boolean positiveResult)256 public void onPinEntered(EditPinPreference preference, boolean positiveResult) { 257 if (!positiveResult) { 258 resetDialogState(); 259 return; 260 } 261 262 mPin = preference.getText(); 263 if (!reasonablePin(mPin)) { 264 // inject error message and display dialog again 265 mError = mRes.getString(R.string.sim_bad_pin); 266 showPinDialog(); 267 return; 268 } 269 switch (mDialogState) { 270 case ICC_LOCK_MODE: 271 tryChangeIccLockState(); 272 break; 273 case ICC_OLD_MODE: 274 mOldPin = mPin; 275 mDialogState = ICC_NEW_MODE; 276 mError = null; 277 mPin = null; 278 showPinDialog(); 279 break; 280 case ICC_NEW_MODE: 281 mNewPin = mPin; 282 mDialogState = ICC_REENTER_MODE; 283 mPin = null; 284 showPinDialog(); 285 break; 286 case ICC_REENTER_MODE: 287 if (!mPin.equals(mNewPin)) { 288 mError = mRes.getString(R.string.sim_pins_dont_match); 289 mDialogState = ICC_NEW_MODE; 290 mPin = null; 291 showPinDialog(); 292 } else { 293 mError = null; 294 tryChangePin(); 295 } 296 break; 297 } 298 } 299 onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)300 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 301 if (preference == mPinToggle) { 302 // Get the new, preferred state 303 mToState = mPinToggle.isChecked(); 304 // Flip it back and pop up pin dialog 305 mPinToggle.setChecked(!mToState); 306 mDialogState = ICC_LOCK_MODE; 307 showPinDialog(); 308 } else if (preference == mPinDialog) { 309 mDialogState = ICC_OLD_MODE; 310 return false; 311 } 312 return true; 313 } 314 tryChangeIccLockState()315 private void tryChangeIccLockState() { 316 // Try to change icc lock. If it succeeds, toggle the lock state and 317 // reset dialog state. Else inject error message and show dialog again. 318 Message callback = Message.obtain(mHandler, ENABLE_ICC_PIN_COMPLETE); 319 mPhone.getIccCard().setIccLockEnabled(mToState, mPin, callback); 320 321 } 322 iccLockChanged(boolean success)323 private void iccLockChanged(boolean success) { 324 if (success) { 325 mPinToggle.setChecked(mToState); 326 } else { 327 Toast.makeText(this, mRes.getString(R.string.sim_lock_failed), Toast.LENGTH_SHORT) 328 .show(); 329 } 330 resetDialogState(); 331 } 332 iccPinChanged(boolean success)333 private void iccPinChanged(boolean success) { 334 if (!success) { 335 Toast.makeText(this, mRes.getString(R.string.sim_change_failed), 336 Toast.LENGTH_SHORT) 337 .show(); 338 } else { 339 Toast.makeText(this, mRes.getString(R.string.sim_change_succeeded), 340 Toast.LENGTH_SHORT) 341 .show(); 342 343 } 344 resetDialogState(); 345 } 346 tryChangePin()347 private void tryChangePin() { 348 Message callback = Message.obtain(mHandler, CHANGE_ICC_PIN_COMPLETE); 349 mPhone.getIccCard().changeIccLockPassword(mOldPin, 350 mNewPin, callback); 351 } 352 reasonablePin(String pin)353 private boolean reasonablePin(String pin) { 354 if (pin == null || pin.length() < MIN_PIN_LENGTH || pin.length() > MAX_PIN_LENGTH) { 355 return false; 356 } else { 357 return true; 358 } 359 } 360 resetDialogState()361 private void resetDialogState() { 362 mError = null; 363 mDialogState = ICC_OLD_MODE; // Default for when Change PIN is clicked 364 mPin = ""; 365 setDialogValues(); 366 mDialogState = OFF_MODE; 367 } 368 } 369