1 /* 2 * Copyright (C) 2006 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.internal.telephony; 18 19 import static android.Manifest.permission.READ_PHONE_STATE; 20 import android.app.ActivityManagerNative; 21 import android.app.AlertDialog; 22 import android.content.Context; 23 import android.content.DialogInterface; 24 import android.content.Intent; 25 import android.content.res.Resources; 26 import android.os.AsyncResult; 27 import android.os.Handler; 28 import android.os.Message; 29 import android.os.Power; 30 import android.os.PowerManager; 31 import android.os.Registrant; 32 import android.os.RegistrantList; 33 import android.util.Log; 34 import android.view.WindowManager; 35 36 import com.android.internal.telephony.PhoneBase; 37 import com.android.internal.telephony.CommandsInterface.RadioState; 38 import com.android.internal.telephony.gsm.SIMRecords; 39 40 import android.os.SystemProperties; 41 42 import com.android.internal.R; 43 44 /** 45 * {@hide} 46 */ 47 public abstract class IccCard { 48 protected String mLogTag; 49 protected boolean mDbg; 50 51 private IccCardStatus mIccCardStatus = null; 52 protected State mState = null; 53 protected PhoneBase mPhone; 54 private RegistrantList mAbsentRegistrants = new RegistrantList(); 55 private RegistrantList mPinLockedRegistrants = new RegistrantList(); 56 private RegistrantList mNetworkLockedRegistrants = new RegistrantList(); 57 58 private boolean mDesiredPinLocked; 59 private boolean mDesiredFdnEnabled; 60 private boolean mIccPinLocked = true; // Default to locked 61 private boolean mIccFdnEnabled = false; // Default to disabled. 62 // Will be updated when SIM_READY. 63 64 65 /* The extra data for broacasting intent INTENT_ICC_STATE_CHANGE */ 66 static public final String INTENT_KEY_ICC_STATE = "ss"; 67 /* NOT_READY means the ICC interface is not ready (eg, radio is off or powering on) */ 68 static public final String INTENT_VALUE_ICC_NOT_READY = "NOT_READY"; 69 /* ABSENT means ICC is missing */ 70 static public final String INTENT_VALUE_ICC_ABSENT = "ABSENT"; 71 /* LOCKED means ICC is locked by pin or by network */ 72 static public final String INTENT_VALUE_ICC_LOCKED = "LOCKED"; 73 /* READY means ICC is ready to access */ 74 static public final String INTENT_VALUE_ICC_READY = "READY"; 75 /* IMSI means ICC IMSI is ready in property */ 76 static public final String INTENT_VALUE_ICC_IMSI = "IMSI"; 77 /* LOADED means all ICC records, including IMSI, are loaded */ 78 static public final String INTENT_VALUE_ICC_LOADED = "LOADED"; 79 /* The extra data for broacasting intent INTENT_ICC_STATE_CHANGE */ 80 static public final String INTENT_KEY_LOCKED_REASON = "reason"; 81 /* PIN means ICC is locked on PIN1 */ 82 static public final String INTENT_VALUE_LOCKED_ON_PIN = "PIN"; 83 /* PUK means ICC is locked on PUK1 */ 84 static public final String INTENT_VALUE_LOCKED_ON_PUK = "PUK"; 85 /* NETWORK means ICC is locked on NETWORK PERSONALIZATION */ 86 static public final String INTENT_VALUE_LOCKED_NETWORK = "NETWORK"; 87 /* PERM_DISABLED means ICC is permanently disabled due to puk fails */ 88 static public final String INTENT_VALUE_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED"; 89 90 91 protected static final int EVENT_ICC_LOCKED_OR_ABSENT = 1; 92 private static final int EVENT_GET_ICC_STATUS_DONE = 2; 93 protected static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 3; 94 private static final int EVENT_PINPUK_DONE = 4; 95 private static final int EVENT_REPOLL_STATUS_DONE = 5; 96 protected static final int EVENT_ICC_READY = 6; 97 private static final int EVENT_QUERY_FACILITY_LOCK_DONE = 7; 98 private static final int EVENT_CHANGE_FACILITY_LOCK_DONE = 8; 99 private static final int EVENT_CHANGE_ICC_PASSWORD_DONE = 9; 100 private static final int EVENT_QUERY_FACILITY_FDN_DONE = 10; 101 private static final int EVENT_CHANGE_FACILITY_FDN_DONE = 11; 102 private static final int EVENT_ICC_STATUS_CHANGED = 12; 103 private static final int EVENT_CARD_REMOVED = 13; 104 private static final int EVENT_CARD_ADDED = 14; 105 106 /* 107 UNKNOWN is a transient state, for example, after uesr inputs ICC pin under 108 PIN_REQUIRED state, the query for ICC status returns UNKNOWN before it 109 turns to READY 110 */ 111 public enum State { 112 UNKNOWN, 113 ABSENT, 114 PIN_REQUIRED, 115 PUK_REQUIRED, 116 NETWORK_LOCKED, 117 READY, 118 NOT_READY, 119 PERM_DISABLED; 120 121 public boolean isPinLocked() { 122 return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED)); 123 } 124 125 public boolean iccCardExist() { 126 return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED) 127 || (this == NETWORK_LOCKED) || (this == READY) 128 || (this == PERM_DISABLED)); 129 } 130 } 131 132 public State getState() { 133 if (mState == null) { 134 switch(mPhone.mCM.getRadioState()) { 135 /* This switch block must not return anything in 136 * State.isLocked() or State.ABSENT. 137 * If it does, handleSimStatus() may break 138 */ 139 case RADIO_OFF: 140 case RADIO_UNAVAILABLE: 141 case SIM_NOT_READY: 142 case RUIM_NOT_READY: 143 return State.UNKNOWN; 144 case SIM_LOCKED_OR_ABSENT: 145 case RUIM_LOCKED_OR_ABSENT: 146 //this should be transient-only 147 return State.UNKNOWN; 148 case SIM_READY: 149 case RUIM_READY: 150 case NV_READY: 151 return State.READY; 152 case NV_NOT_READY: 153 return State.ABSENT; 154 } 155 } else { 156 return mState; 157 } 158 159 Log.e(mLogTag, "IccCard.getState(): case should never be reached"); 160 return State.UNKNOWN; 161 } 162 163 public IccCard(PhoneBase phone, String logTag, Boolean dbg) { 164 mPhone = phone; 165 mPhone.mCM.registerForIccStatusChanged(mHandler, EVENT_ICC_STATUS_CHANGED, null); 166 mLogTag = logTag; 167 mDbg = dbg; 168 } 169 170 public void dispose() { 171 mPhone.mCM.unregisterForIccStatusChanged(mHandler); 172 } 173 174 protected void finalize() { 175 if(mDbg) Log.d(mLogTag, "IccCard finalized"); 176 } 177 178 /** 179 * Notifies handler of any transition into State.ABSENT 180 */ 181 public void registerForAbsent(Handler h, int what, Object obj) { 182 Registrant r = new Registrant (h, what, obj); 183 184 mAbsentRegistrants.add(r); 185 186 if (getState() == State.ABSENT) { 187 r.notifyRegistrant(); 188 } 189 } 190 191 public void unregisterForAbsent(Handler h) { 192 mAbsentRegistrants.remove(h); 193 } 194 195 /** 196 * Notifies handler of any transition into State.NETWORK_LOCKED 197 */ 198 public void registerForNetworkLocked(Handler h, int what, Object obj) { 199 Registrant r = new Registrant (h, what, obj); 200 201 mNetworkLockedRegistrants.add(r); 202 203 if (getState() == State.NETWORK_LOCKED) { 204 r.notifyRegistrant(); 205 } 206 } 207 208 public void unregisterForNetworkLocked(Handler h) { 209 mNetworkLockedRegistrants.remove(h); 210 } 211 212 /** 213 * Notifies handler of any transition into State.isPinLocked() 214 */ 215 public void registerForLocked(Handler h, int what, Object obj) { 216 Registrant r = new Registrant (h, what, obj); 217 218 mPinLockedRegistrants.add(r); 219 220 if (getState().isPinLocked()) { 221 r.notifyRegistrant(); 222 } 223 } 224 225 public void unregisterForLocked(Handler h) { 226 mPinLockedRegistrants.remove(h); 227 } 228 229 230 /** 231 * Supply the ICC PIN to the ICC 232 * 233 * When the operation is complete, onComplete will be sent to its 234 * Handler. 235 * 236 * onComplete.obj will be an AsyncResult 237 * 238 * ((AsyncResult)onComplete.obj).exception == null on success 239 * ((AsyncResult)onComplete.obj).exception != null on fail 240 * 241 * If the supplied PIN is incorrect: 242 * ((AsyncResult)onComplete.obj).exception != null 243 * && ((AsyncResult)onComplete.obj).exception 244 * instanceof com.android.internal.telephony.gsm.CommandException) 245 * && ((CommandException)(((AsyncResult)onComplete.obj).exception)) 246 * .getCommandError() == CommandException.Error.PASSWORD_INCORRECT 247 * 248 * 249 */ 250 251 public void supplyPin (String pin, Message onComplete) { 252 mPhone.mCM.supplyIccPin(pin, mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete)); 253 } 254 255 public void supplyPuk (String puk, String newPin, Message onComplete) { 256 mPhone.mCM.supplyIccPuk(puk, newPin, 257 mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete)); 258 } 259 260 public void supplyPin2 (String pin2, Message onComplete) { 261 mPhone.mCM.supplyIccPin2(pin2, 262 mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete)); 263 } 264 265 public void supplyPuk2 (String puk2, String newPin2, Message onComplete) { 266 mPhone.mCM.supplyIccPuk2(puk2, newPin2, 267 mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete)); 268 } 269 270 public void supplyNetworkDepersonalization (String pin, Message onComplete) { 271 mPhone.mCM.supplyNetworkDepersonalization(pin, 272 mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete)); 273 } 274 275 /** 276 * Check whether ICC pin lock is enabled 277 * This is a sync call which returns the cached pin enabled state 278 * 279 * @return true for ICC locked enabled 280 * false for ICC locked disabled 281 */ 282 public boolean getIccLockEnabled() { 283 return mIccPinLocked; 284 } 285 286 /** 287 * Check whether ICC fdn (fixed dialing number) is enabled 288 * This is a sync call which returns the cached pin enabled state 289 * 290 * @return true for ICC fdn enabled 291 * false for ICC fdn disabled 292 */ 293 public boolean getIccFdnEnabled() { 294 return mIccFdnEnabled; 295 } 296 297 /** 298 * Set the ICC pin lock enabled or disabled 299 * When the operation is complete, onComplete will be sent to its handler 300 * 301 * @param enabled "true" for locked "false" for unlocked. 302 * @param password needed to change the ICC pin state, aka. Pin1 303 * @param onComplete 304 * onComplete.obj will be an AsyncResult 305 * ((AsyncResult)onComplete.obj).exception == null on success 306 * ((AsyncResult)onComplete.obj).exception != null on fail 307 */ 308 public void setIccLockEnabled (boolean enabled, 309 String password, Message onComplete) { 310 int serviceClassX; 311 serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE + 312 CommandsInterface.SERVICE_CLASS_DATA + 313 CommandsInterface.SERVICE_CLASS_FAX; 314 315 mDesiredPinLocked = enabled; 316 317 mPhone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_SIM, 318 enabled, password, serviceClassX, 319 mHandler.obtainMessage(EVENT_CHANGE_FACILITY_LOCK_DONE, onComplete)); 320 } 321 322 /** 323 * Set the ICC fdn enabled or disabled 324 * When the operation is complete, onComplete will be sent to its handler 325 * 326 * @param enabled "true" for locked "false" for unlocked. 327 * @param password needed to change the ICC fdn enable, aka Pin2 328 * @param onComplete 329 * onComplete.obj will be an AsyncResult 330 * ((AsyncResult)onComplete.obj).exception == null on success 331 * ((AsyncResult)onComplete.obj).exception != null on fail 332 */ 333 public void setIccFdnEnabled (boolean enabled, 334 String password, Message onComplete) { 335 int serviceClassX; 336 serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE + 337 CommandsInterface.SERVICE_CLASS_DATA + 338 CommandsInterface.SERVICE_CLASS_FAX + 339 CommandsInterface.SERVICE_CLASS_SMS; 340 341 mDesiredFdnEnabled = enabled; 342 343 mPhone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_FD, 344 enabled, password, serviceClassX, 345 mHandler.obtainMessage(EVENT_CHANGE_FACILITY_FDN_DONE, onComplete)); 346 } 347 348 /** 349 * Change the ICC password used in ICC pin lock 350 * When the operation is complete, onComplete will be sent to its handler 351 * 352 * @param oldPassword is the old password 353 * @param newPassword is the new password 354 * @param onComplete 355 * onComplete.obj will be an AsyncResult 356 * ((AsyncResult)onComplete.obj).exception == null on success 357 * ((AsyncResult)onComplete.obj).exception != null on fail 358 */ 359 public void changeIccLockPassword(String oldPassword, String newPassword, 360 Message onComplete) { 361 mPhone.mCM.changeIccPin(oldPassword, newPassword, 362 mHandler.obtainMessage(EVENT_CHANGE_ICC_PASSWORD_DONE, onComplete)); 363 364 } 365 366 /** 367 * Change the ICC password used in ICC fdn enable 368 * When the operation is complete, onComplete will be sent to its handler 369 * 370 * @param oldPassword is the old password 371 * @param newPassword is the new password 372 * @param onComplete 373 * onComplete.obj will be an AsyncResult 374 * ((AsyncResult)onComplete.obj).exception == null on success 375 * ((AsyncResult)onComplete.obj).exception != null on fail 376 */ 377 public void changeIccFdnPassword(String oldPassword, String newPassword, 378 Message onComplete) { 379 mPhone.mCM.changeIccPin2(oldPassword, newPassword, 380 mHandler.obtainMessage(EVENT_CHANGE_ICC_PASSWORD_DONE, onComplete)); 381 382 } 383 384 385 /** 386 * Returns service provider name stored in ICC card. 387 * If there is no service provider name associated or the record is not 388 * yet available, null will be returned <p> 389 * 390 * Please use this value when display Service Provider Name in idle mode <p> 391 * 392 * Usage of this provider name in the UI is a common carrier requirement. 393 * 394 * Also available via Android property "gsm.sim.operator.alpha" 395 * 396 * @return Service Provider Name stored in ICC card 397 * null if no service provider name associated or the record is not 398 * yet available 399 * 400 */ 401 public abstract String getServiceProviderName(); 402 403 protected void updateStateProperty() { 404 mPhone.setSystemProperty(TelephonyProperties.PROPERTY_SIM_STATE, getState().toString()); 405 } 406 407 private void getIccCardStatusDone(AsyncResult ar) { 408 if (ar.exception != null) { 409 Log.e(mLogTag,"Error getting ICC status. " 410 + "RIL_REQUEST_GET_ICC_STATUS should " 411 + "never return an error", ar.exception); 412 return; 413 } 414 handleIccCardStatus((IccCardStatus) ar.result); 415 } 416 417 private void handleIccCardStatus(IccCardStatus newCardStatus) { 418 boolean transitionedIntoPinLocked; 419 boolean transitionedIntoAbsent; 420 boolean transitionedIntoNetworkLocked; 421 boolean transitionedIntoPermBlocked; 422 boolean isIccCardRemoved; 423 boolean isIccCardAdded; 424 425 State oldState, newState; 426 427 oldState = mState; 428 mIccCardStatus = newCardStatus; 429 newState = getIccCardState(); 430 mState = newState; 431 432 updateStateProperty(); 433 434 transitionedIntoPinLocked = ( 435 (oldState != State.PIN_REQUIRED && newState == State.PIN_REQUIRED) 436 || (oldState != State.PUK_REQUIRED && newState == State.PUK_REQUIRED)); 437 transitionedIntoAbsent = (oldState != State.ABSENT && newState == State.ABSENT); 438 transitionedIntoNetworkLocked = (oldState != State.NETWORK_LOCKED 439 && newState == State.NETWORK_LOCKED); 440 transitionedIntoPermBlocked = (oldState != State.PERM_DISABLED 441 && newState == State.PERM_DISABLED); 442 isIccCardRemoved = (oldState != null && 443 oldState.iccCardExist() && newState == State.ABSENT); 444 isIccCardAdded = (oldState == State.ABSENT && 445 newState != null && newState.iccCardExist()); 446 447 if (transitionedIntoPinLocked) { 448 if (mDbg) log("Notify SIM pin or puk locked."); 449 mPinLockedRegistrants.notifyRegistrants(); 450 broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED, 451 (newState == State.PIN_REQUIRED) ? 452 INTENT_VALUE_LOCKED_ON_PIN : INTENT_VALUE_LOCKED_ON_PUK); 453 } else if (transitionedIntoAbsent) { 454 if (mDbg) log("Notify SIM missing."); 455 mAbsentRegistrants.notifyRegistrants(); 456 broadcastIccStateChangedIntent(INTENT_VALUE_ICC_ABSENT, null); 457 } else if (transitionedIntoNetworkLocked) { 458 if (mDbg) log("Notify SIM network locked."); 459 mNetworkLockedRegistrants.notifyRegistrants(); 460 broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED, 461 INTENT_VALUE_LOCKED_NETWORK); 462 } else if (transitionedIntoPermBlocked) { 463 if (mDbg) log("Notify SIM permanently disabled."); 464 broadcastIccStateChangedIntent(INTENT_VALUE_ICC_ABSENT, 465 INTENT_VALUE_ABSENT_ON_PERM_DISABLED); 466 } 467 468 if (isIccCardRemoved) { 469 mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_REMOVED, null)); 470 } else if (isIccCardAdded) { 471 mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null)); 472 } 473 } 474 475 private void onIccSwap(boolean isAdded) { 476 // TODO: Here we assume the device can't handle SIM hot-swap 477 // and has to reboot. We may want to add a property, 478 // e.g. REBOOT_ON_SIM_SWAP, to indicate if modem support 479 // hot-swap. 480 DialogInterface.OnClickListener listener = null; 481 482 483 // TODO: SimRecords is not reset while SIM ABSENT (only reset while 484 // Radio_off_or_not_available). Have to reset in both both 485 // added or removed situation. 486 listener = new DialogInterface.OnClickListener() { 487 @Override 488 public void onClick(DialogInterface dialog, int which) { 489 if (which == DialogInterface.BUTTON_POSITIVE) { 490 if (mDbg) log("Reboot due to SIM swap"); 491 PowerManager pm = (PowerManager) mPhone.getContext() 492 .getSystemService(Context.POWER_SERVICE); 493 pm.reboot("SIM is added."); 494 } 495 } 496 497 }; 498 499 Resources r = Resources.getSystem(); 500 501 String title = (isAdded) ? r.getString(R.string.sim_added_title) : 502 r.getString(R.string.sim_removed_title); 503 String message = (isAdded) ? r.getString(R.string.sim_added_message) : 504 r.getString(R.string.sim_removed_message); 505 String buttonTxt = r.getString(R.string.sim_restart_button); 506 507 AlertDialog dialog = new AlertDialog.Builder(mPhone.getContext()) 508 .setTitle(title) 509 .setMessage(message) 510 .setPositiveButton(buttonTxt, listener) 511 .create(); 512 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 513 dialog.show(); 514 } 515 516 /** 517 * Interperate EVENT_QUERY_FACILITY_LOCK_DONE 518 * @param ar is asyncResult of Query_Facility_Locked 519 */ 520 private void onQueryFdnEnabled(AsyncResult ar) { 521 if(ar.exception != null) { 522 if(mDbg) log("Error in querying facility lock:" + ar.exception); 523 return; 524 } 525 526 int[] ints = (int[])ar.result; 527 if(ints.length != 0) { 528 mIccFdnEnabled = (0!=ints[0]); 529 if(mDbg) log("Query facility lock : " + mIccFdnEnabled); 530 } else { 531 Log.e(mLogTag, "[IccCard] Bogus facility lock response"); 532 } 533 } 534 535 /** 536 * Interperate EVENT_QUERY_FACILITY_LOCK_DONE 537 * @param ar is asyncResult of Query_Facility_Locked 538 */ 539 private void onQueryFacilityLock(AsyncResult ar) { 540 if(ar.exception != null) { 541 if (mDbg) log("Error in querying facility lock:" + ar.exception); 542 return; 543 } 544 545 int[] ints = (int[])ar.result; 546 if(ints.length != 0) { 547 mIccPinLocked = (0!=ints[0]); 548 if(mDbg) log("Query facility lock : " + mIccPinLocked); 549 } else { 550 Log.e(mLogTag, "[IccCard] Bogus facility lock response"); 551 } 552 } 553 554 public void broadcastIccStateChangedIntent(String value, String reason) { 555 Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED); 556 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 557 intent.putExtra(Phone.PHONE_NAME_KEY, mPhone.getPhoneName()); 558 intent.putExtra(INTENT_KEY_ICC_STATE, value); 559 intent.putExtra(INTENT_KEY_LOCKED_REASON, reason); 560 if(mDbg) log("Broadcasting intent ACTION_SIM_STATE_CHANGED " + value 561 + " reason " + reason); 562 ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE); 563 } 564 565 protected Handler mHandler = new Handler() { 566 @Override 567 public void handleMessage(Message msg){ 568 AsyncResult ar; 569 int serviceClassX; 570 571 serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE + 572 CommandsInterface.SERVICE_CLASS_DATA + 573 CommandsInterface.SERVICE_CLASS_FAX; 574 575 if (!mPhone.mIsTheCurrentActivePhone) { 576 Log.e(mLogTag, "Received message " + msg + "[" + msg.what 577 + "] while being destroyed. Ignoring."); 578 return; 579 } 580 581 switch (msg.what) { 582 case EVENT_RADIO_OFF_OR_NOT_AVAILABLE: 583 mState = null; 584 updateStateProperty(); 585 broadcastIccStateChangedIntent(INTENT_VALUE_ICC_NOT_READY, null); 586 break; 587 case EVENT_ICC_READY: 588 //TODO: put facility read in SIM_READY now, maybe in REG_NW 589 mPhone.mCM.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE)); 590 mPhone.mCM.queryFacilityLock ( 591 CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX, 592 obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE)); 593 mPhone.mCM.queryFacilityLock ( 594 CommandsInterface.CB_FACILITY_BA_FD, "", serviceClassX, 595 obtainMessage(EVENT_QUERY_FACILITY_FDN_DONE)); 596 break; 597 case EVENT_ICC_LOCKED_OR_ABSENT: 598 mPhone.mCM.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE)); 599 mPhone.mCM.queryFacilityLock ( 600 CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX, 601 obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE)); 602 break; 603 case EVENT_GET_ICC_STATUS_DONE: 604 ar = (AsyncResult)msg.obj; 605 606 getIccCardStatusDone(ar); 607 break; 608 case EVENT_PINPUK_DONE: 609 // a PIN/PUK/PIN2/PUK2/Network Personalization 610 // request has completed. ar.userObj is the response Message 611 // Repoll before returning 612 ar = (AsyncResult)msg.obj; 613 // TODO should abstract these exceptions 614 AsyncResult.forMessage(((Message)ar.userObj)).exception 615 = ar.exception; 616 mPhone.mCM.getIccCardStatus( 617 obtainMessage(EVENT_REPOLL_STATUS_DONE, ar.userObj)); 618 break; 619 case EVENT_REPOLL_STATUS_DONE: 620 // Finished repolling status after PIN operation 621 // ar.userObj is the response messaeg 622 // ar.userObj.obj is already an AsyncResult with an 623 // appropriate exception filled in if applicable 624 625 ar = (AsyncResult)msg.obj; 626 getIccCardStatusDone(ar); 627 ((Message)ar.userObj).sendToTarget(); 628 break; 629 case EVENT_QUERY_FACILITY_LOCK_DONE: 630 ar = (AsyncResult)msg.obj; 631 onQueryFacilityLock(ar); 632 break; 633 case EVENT_QUERY_FACILITY_FDN_DONE: 634 ar = (AsyncResult)msg.obj; 635 onQueryFdnEnabled(ar); 636 break; 637 case EVENT_CHANGE_FACILITY_LOCK_DONE: 638 ar = (AsyncResult)msg.obj; 639 if (ar.exception == null) { 640 mIccPinLocked = mDesiredPinLocked; 641 if (mDbg) log( "EVENT_CHANGE_FACILITY_LOCK_DONE: " + 642 "mIccPinLocked= " + mIccPinLocked); 643 } else { 644 Log.e(mLogTag, "Error change facility lock with exception " 645 + ar.exception); 646 } 647 AsyncResult.forMessage(((Message)ar.userObj)).exception 648 = ar.exception; 649 ((Message)ar.userObj).sendToTarget(); 650 break; 651 case EVENT_CHANGE_FACILITY_FDN_DONE: 652 ar = (AsyncResult)msg.obj; 653 654 if (ar.exception == null) { 655 mIccFdnEnabled = mDesiredFdnEnabled; 656 if (mDbg) log("EVENT_CHANGE_FACILITY_FDN_DONE: " + 657 "mIccFdnEnabled=" + mIccFdnEnabled); 658 } else { 659 Log.e(mLogTag, "Error change facility fdn with exception " 660 + ar.exception); 661 } 662 AsyncResult.forMessage(((Message)ar.userObj)).exception 663 = ar.exception; 664 ((Message)ar.userObj).sendToTarget(); 665 break; 666 case EVENT_CHANGE_ICC_PASSWORD_DONE: 667 ar = (AsyncResult)msg.obj; 668 if(ar.exception != null) { 669 Log.e(mLogTag, "Error in change sim password with exception" 670 + ar.exception); 671 } 672 AsyncResult.forMessage(((Message)ar.userObj)).exception 673 = ar.exception; 674 ((Message)ar.userObj).sendToTarget(); 675 break; 676 case EVENT_ICC_STATUS_CHANGED: 677 Log.d(mLogTag, "Received Event EVENT_ICC_STATUS_CHANGED"); 678 mPhone.mCM.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE)); 679 break; 680 case EVENT_CARD_REMOVED: 681 onIccSwap(false); 682 break; 683 case EVENT_CARD_ADDED: 684 onIccSwap(true); 685 break; 686 default: 687 Log.e(mLogTag, "[IccCard] Unknown Event " + msg.what); 688 } 689 } 690 }; 691 692 public State getIccCardState() { 693 if (mIccCardStatus == null) { 694 Log.e(mLogTag, "[IccCard] IccCardStatus is null"); 695 return IccCard.State.ABSENT; 696 } 697 698 // this is common for all radio technologies 699 if (!mIccCardStatus.getCardState().isCardPresent()) { 700 return IccCard.State.ABSENT; 701 } 702 703 RadioState currentRadioState = mPhone.mCM.getRadioState(); 704 // check radio technology 705 if( currentRadioState == RadioState.RADIO_OFF || 706 currentRadioState == RadioState.RADIO_UNAVAILABLE || 707 currentRadioState == RadioState.SIM_NOT_READY || 708 currentRadioState == RadioState.RUIM_NOT_READY || 709 currentRadioState == RadioState.NV_NOT_READY || 710 currentRadioState == RadioState.NV_READY) { 711 return IccCard.State.NOT_READY; 712 } 713 714 if( currentRadioState == RadioState.SIM_LOCKED_OR_ABSENT || 715 currentRadioState == RadioState.SIM_READY || 716 currentRadioState == RadioState.RUIM_LOCKED_OR_ABSENT || 717 currentRadioState == RadioState.RUIM_READY) { 718 719 State csimState = 720 getAppState(mIccCardStatus.getCdmaSubscriptionAppIndex()); 721 State usimState = 722 getAppState(mIccCardStatus.getGsmUmtsSubscriptionAppIndex()); 723 724 if(mDbg) log("USIM=" + usimState + " CSIM=" + csimState); 725 726 if (mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) { 727 // UICC card contains both USIM and CSIM 728 // Return consolidated status 729 return getConsolidatedState(csimState, usimState, csimState); 730 } 731 732 // check for CDMA radio technology 733 if (currentRadioState == RadioState.RUIM_LOCKED_OR_ABSENT || 734 currentRadioState == RadioState.RUIM_READY) { 735 return csimState; 736 } 737 return usimState; 738 } 739 740 return IccCard.State.ABSENT; 741 } 742 743 private State getAppState(int appIndex) { 744 IccCardApplication app; 745 if (appIndex >= 0 && appIndex < IccCardStatus.CARD_MAX_APPS) { 746 app = mIccCardStatus.getApplication(appIndex); 747 } else { 748 Log.e(mLogTag, "[IccCard] Invalid Subscription Application index:" + appIndex); 749 return IccCard.State.ABSENT; 750 } 751 752 if (app == null) { 753 Log.e(mLogTag, "[IccCard] Subscription Application in not present"); 754 return IccCard.State.ABSENT; 755 } 756 757 // check if PIN required 758 if (app.pin1.isPermBlocked()) { 759 return IccCard.State.PERM_DISABLED; 760 } 761 if (app.app_state.isPinRequired()) { 762 return IccCard.State.PIN_REQUIRED; 763 } 764 if (app.app_state.isPukRequired()) { 765 return IccCard.State.PUK_REQUIRED; 766 } 767 if (app.app_state.isSubscriptionPersoEnabled()) { 768 return IccCard.State.NETWORK_LOCKED; 769 } 770 if (app.app_state.isAppReady()) { 771 return IccCard.State.READY; 772 } 773 if (app.app_state.isAppNotReady()) { 774 return IccCard.State.NOT_READY; 775 } 776 return IccCard.State.NOT_READY; 777 } 778 779 private State getConsolidatedState(State left, State right, State preferredState) { 780 // Check if either is absent. 781 if (right == IccCard.State.ABSENT) return left; 782 if (left == IccCard.State.ABSENT) return right; 783 784 // Only if both are ready, return ready 785 if ((left == IccCard.State.READY) && (right == IccCard.State.READY)) { 786 return State.READY; 787 } 788 789 // Case one is ready, but the other is not. 790 if (((right == IccCard.State.NOT_READY) && (left == IccCard.State.READY)) || 791 ((left == IccCard.State.NOT_READY) && (right == IccCard.State.READY))) { 792 return IccCard.State.NOT_READY; 793 } 794 795 // At this point, the other state is assumed to be one of locked state 796 if (right == IccCard.State.NOT_READY) return left; 797 if (left == IccCard.State.NOT_READY) return right; 798 799 // At this point, FW currently just assumes the status will be 800 // consistent across the applications... 801 return preferredState; 802 } 803 804 public boolean isApplicationOnIcc(IccCardApplication.AppType type) { 805 if (mIccCardStatus == null) return false; 806 807 for (int i = 0 ; i < mIccCardStatus.getNumApplications(); i++) { 808 IccCardApplication app = mIccCardStatus.getApplication(i); 809 if (app != null && app.app_type == type) { 810 return true; 811 } 812 } 813 return false; 814 } 815 816 /** 817 * @return true if a ICC card is present 818 */ 819 public boolean hasIccCard() { 820 if (mIccCardStatus == null) { 821 return false; 822 } else { 823 // Returns ICC card status for both GSM and CDMA mode 824 return mIccCardStatus.getCardState().isCardPresent(); 825 } 826 } 827 828 private void log(String msg) { 829 Log.d(mLogTag, "[IccCard] " + msg); 830 } 831 } 832