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 17package com.android.settings; 18 19import android.content.BroadcastReceiver; 20import android.content.Context; 21import android.content.Intent; 22import android.content.IntentFilter; 23import android.content.res.Resources; 24import android.os.AsyncResult; 25import android.os.Bundle; 26import android.os.Handler; 27import android.os.Message; 28import android.preference.CheckBoxPreference; 29import android.preference.Preference; 30import android.preference.PreferenceActivity; 31import android.preference.PreferenceScreen; 32import android.widget.Toast; 33 34import com.android.internal.telephony.Phone; 35import com.android.internal.telephony.PhoneFactory; 36import 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 */ 47public 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 352 } 353 354 private void iccLockChanged(boolean success) { 355 if (success) { 356 mPinToggle.setChecked(mToState); 357 } else { 358 Toast.makeText(this, mRes.getString(R.string.sim_lock_failed), Toast.LENGTH_SHORT) 359 .show(); 360 } 361 resetDialogState(); 362 } 363 364 private void iccPinChanged(boolean success) { 365 if (!success) { 366 Toast.makeText(this, mRes.getString(R.string.sim_change_failed), 367 Toast.LENGTH_SHORT) 368 .show(); 369 } else { 370 Toast.makeText(this, mRes.getString(R.string.sim_change_succeeded), 371 Toast.LENGTH_SHORT) 372 .show(); 373 374 } 375 resetDialogState(); 376 } 377 378 private void tryChangePin() { 379 Message callback = Message.obtain(mHandler, MSG_CHANGE_ICC_PIN_COMPLETE); 380 mPhone.getIccCard().changeIccLockPassword(mOldPin, 381 mNewPin, callback); 382 } 383 384 private boolean reasonablePin(String pin) { 385 if (pin == null || pin.length() < MIN_PIN_LENGTH || pin.length() > MAX_PIN_LENGTH) { 386 return false; 387 } else { 388 return true; 389 } 390 } 391 392 private void resetDialogState() { 393 mError = null; 394 mDialogState = ICC_OLD_MODE; // Default for when Change PIN is clicked 395 mPin = ""; 396 setDialogValues(); 397 mDialogState = OFF_MODE; 398 } 399} 400