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