2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.internal.telephony;
19 import static android.Manifest.permission.READ_PHONE_STATE;
20 import android.app.ActivityManagerNative;
21 import android.content.Intent;
22 import android.os.AsyncResult;
23 import android.os.Handler;
24 import android.os.Message;
25 import android.os.Registrant;
26 import android.os.RegistrantList;
27 import android.util.Log;
29 import com.android.internal.telephony.PhoneBase;
30 import com.android.internal.telephony.CommandsInterface.RadioState;
35 public abstract class IccCard {
36 protected String mLogTag;
37 protected boolean mDbg;
39 private IccCardStatus mIccCardStatus = null;
40 protected State mState = null;
41 protected PhoneBase mPhone;
42 private RegistrantList mAbsentRegistrants = new RegistrantList();
43 private RegistrantList mPinLockedRegistrants = new RegistrantList();
44 private RegistrantList mNetworkLockedRegistrants = new RegistrantList();
46 private boolean mDesiredPinLocked;
47 private boolean mDesiredFdnEnabled;
48 private boolean mIccPinLocked = true; // Default to locked
49 private boolean mIccFdnEnabled = false; // Default to disabled.
50 // Will be updated when SIM_READY.
53 /* The extra data for broacasting intent INTENT_ICC_STATE_CHANGE */
54 static public final String INTENT_KEY_ICC_STATE = "ss";
55 /* NOT_READY means the ICC interface is not ready (eg, radio is off or powering on) */
56 static public final String INTENT_VALUE_ICC_NOT_READY = "NOT_READY";
57 /* ABSENT means ICC is missing */
58 static public final String INTENT_VALUE_ICC_ABSENT = "ABSENT";
59 /* LOCKED means ICC is locked by pin or by network */
60 static public final String INTENT_VALUE_ICC_LOCKED = "LOCKED";
61 /* READY means ICC is ready to access */
62 static public final String INTENT_VALUE_ICC_READY = "READY";
63 /* IMSI means ICC IMSI is ready in property */
64 static public final String INTENT_VALUE_ICC_IMSI = "IMSI";
65 /* LOADED means all ICC records, including IMSI, are loaded */
66 static public final String INTENT_VALUE_ICC_LOADED = "LOADED";
67 /* The extra data for broacasting intent INTENT_ICC_STATE_CHANGE */
68 static public final String INTENT_KEY_LOCKED_REASON = "reason";
69 /* PIN means ICC is locked on PIN1 */
70 static public final String INTENT_VALUE_LOCKED_ON_PIN = "PIN";
71 /* PUK means ICC is locked on PUK1 */
72 static public final String INTENT_VALUE_LOCKED_ON_PUK = "PUK";
73 /* NETWORK means ICC is locked on NETWORK PERSONALIZATION */
74 static public final String INTENT_VALUE_LOCKED_NETWORK = "NETWORK";
76 protected static final int EVENT_ICC_LOCKED_OR_ABSENT = 1;
77 private static final int EVENT_GET_ICC_STATUS_DONE = 2;
78 protected static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 3;
79 private static final int EVENT_PINPUK_DONE = 4;
80 private static final int EVENT_REPOLL_STATUS_DONE = 5;
81 protected static final int EVENT_ICC_READY = 6;
82 private static final int EVENT_QUERY_FACILITY_LOCK_DONE = 7;
83 private static final int EVENT_CHANGE_FACILITY_LOCK_DONE = 8;
84 private static final int EVENT_CHANGE_ICC_PASSWORD_DONE = 9;
85 private static final int EVENT_QUERY_FACILITY_FDN_DONE = 10;
86 private static final int EVENT_CHANGE_FACILITY_FDN_DONE = 11;
89 UNKNOWN is a transient state, for example, after uesr inputs ICC pin under
90 PIN_REQUIRED state, the query for ICC status returns UNKNOWN before it
102 public boolean isPinLocked() {
103 return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED));
107 public State getState() {
108 if (mState == null) {
109 switch(mPhone.mCM.getRadioState()) {
110 /* This switch block must not return anything in
111 * State.isLocked() or State.ABSENT.
112 * If it does, handleSimStatus() may break
115 case RADIO_UNAVAILABLE:
118 return State.UNKNOWN;
119 case SIM_LOCKED_OR_ABSENT:
120 case RUIM_LOCKED_OR_ABSENT:
121 //this should be transient-only
122 return State.UNKNOWN;
134 Log.e(mLogTag, "IccCard.getState(): case should never be reached");
135 return State.UNKNOWN;
138 public IccCard(PhoneBase phone, String logTag, Boolean dbg) {
144 abstract public void dispose();
146 protected void finalize() {
147 if(mDbg) Log.d(mLogTag, "IccCard finalized");
151 * Notifies handler of any transition into State.ABSENT
153 public void registerForAbsent(Handler h, int what, Object obj) {
154 Registrant r = new Registrant (h, what, obj);
156 mAbsentRegistrants.add(r);
158 if (getState() == State.ABSENT) {
159 r.notifyRegistrant();
163 public void unregisterForAbsent(Handler h) {
164 mAbsentRegistrants.remove(h);
168 * Notifies handler of any transition into State.NETWORK_LOCKED
170 public void registerForNetworkLocked(Handler h, int what, Object obj) {
171 Registrant r = new Registrant (h, what, obj);
173 mNetworkLockedRegistrants.add(r);
175 if (getState() == State.NETWORK_LOCKED) {
176 r.notifyRegistrant();
180 public void unregisterForNetworkLocked(Handler h) {
181 mNetworkLockedRegistrants.remove(h);
185 * Notifies handler of any transition into State.isPinLocked()
187 public void registerForLocked(Handler h, int what, Object obj) {
188 Registrant r = new Registrant (h, what, obj);
190 mPinLockedRegistrants.add(r);
192 if (getState().isPinLocked()) {
193 r.notifyRegistrant();
197 public void unregisterForLocked(Handler h) {
198 mPinLockedRegistrants.remove(h);
203 * Supply the ICC PIN to the ICC
205 * When the operation is complete, onComplete will be sent to its
208 * onComplete.obj will be an AsyncResult
210 * ((AsyncResult)onComplete.obj).exception == null on success
211 * ((AsyncResult)onComplete.obj).exception != null on fail
213 * If the supplied PIN is incorrect:
214 * ((AsyncResult)onComplete.obj).exception != null
215 * && ((AsyncResult)onComplete.obj).exception
216 * instanceof com.android.internal.telephony.gsm.CommandException)
217 * && ((CommandException)(((AsyncResult)onComplete.obj).exception))
218 * .getCommandError() == CommandException.Error.PASSWORD_INCORRECT
223 public void supplyPin (String pin, Message onComplete) {
224 mPhone.mCM.supplyIccPin(pin, mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
227 public void supplyPuk (String puk, String newPin, Message onComplete) {
228 mPhone.mCM.supplyIccPuk(puk, newPin,
229 mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
232 public void supplyPin2 (String pin2, Message onComplete) {
233 mPhone.mCM.supplyIccPin2(pin2,
234 mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
237 public void supplyPuk2 (String puk2, String newPin2, Message onComplete) {
238 mPhone.mCM.supplyIccPuk2(puk2, newPin2,
239 mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
242 public void supplyNetworkDepersonalization (String pin, Message onComplete) {
243 if(mDbg) log("Network Despersonalization: " + pin);
244 mPhone.mCM.supplyNetworkDepersonalization(pin,
245 mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
249 * Check whether ICC pin lock is enabled
250 * This is a sync call which returns the cached pin enabled state
252 * @return true for ICC locked enabled
253 * false for ICC locked disabled
255 public boolean getIccLockEnabled() {
256 return mIccPinLocked;
260 * Check whether ICC fdn (fixed dialing number) is enabled
261 * This is a sync call which returns the cached pin enabled state
263 * @return true for ICC fdn enabled
264 * false for ICC fdn disabled
266 public boolean getIccFdnEnabled() {
267 return mIccFdnEnabled;
271 * Set the ICC pin lock enabled or disabled
272 * When the operation is complete, onComplete will be sent to its handler
274 * @param enabled "true" for locked "false" for unlocked.
275 * @param password needed to change the ICC pin state, aka. Pin1
277 * onComplete.obj will be an AsyncResult
278 * ((AsyncResult)onComplete.obj).exception == null on success
279 * ((AsyncResult)onComplete.obj).exception != null on fail
281 public void setIccLockEnabled (boolean enabled,
282 String password, Message onComplete) {
284 serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
285 CommandsInterface.SERVICE_CLASS_DATA +
286 CommandsInterface.SERVICE_CLASS_FAX;
288 mDesiredPinLocked = enabled;
290 mPhone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_SIM,
291 enabled, password, serviceClassX,
292 mHandler.obtainMessage(EVENT_CHANGE_FACILITY_LOCK_DONE, onComplete));
296 * Set the ICC fdn enabled or disabled
297 * When the operation is complete, onComplete will be sent to its handler
299 * @param enabled "true" for locked "false" for unlocked.
300 * @param password needed to change the ICC fdn enable, aka Pin2
302 * onComplete.obj will be an AsyncResult
303 * ((AsyncResult)onComplete.obj).exception == null on success
304 * ((AsyncResult)onComplete.obj).exception != null on fail
306 public void setIccFdnEnabled (boolean enabled,
307 String password, Message onComplete) {
309 serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
310 CommandsInterface.SERVICE_CLASS_DATA +
311 CommandsInterface.SERVICE_CLASS_FAX +
312 CommandsInterface.SERVICE_CLASS_SMS;
314 mDesiredFdnEnabled = enabled;
316 mPhone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_FD,
317 enabled, password, serviceClassX,
318 mHandler.obtainMessage(EVENT_CHANGE_FACILITY_FDN_DONE, onComplete));
322 * Change the ICC password used in ICC pin lock
323 * When the operation is complete, onComplete will be sent to its handler
325 * @param oldPassword is the old password
326 * @param newPassword is the new password
328 * onComplete.obj will be an AsyncResult
329 * ((AsyncResult)onComplete.obj).exception == null on success
330 * ((AsyncResult)onComplete.obj).exception != null on fail
332 public void changeIccLockPassword(String oldPassword, String newPassword,
333 Message onComplete) {
334 if(mDbg) log("Change Pin1 old: " + oldPassword + " new: " + newPassword);
335 mPhone.mCM.changeIccPin(oldPassword, newPassword,
336 mHandler.obtainMessage(EVENT_CHANGE_ICC_PASSWORD_DONE, onComplete));
341 * Change the ICC password used in ICC fdn enable
342 * When the operation is complete, onComplete will be sent to its handler
344 * @param oldPassword is the old password
345 * @param newPassword is the new password
347 * onComplete.obj will be an AsyncResult
348 * ((AsyncResult)onComplete.obj).exception == null on success
349 * ((AsyncResult)onComplete.obj).exception != null on fail
351 public void changeIccFdnPassword(String oldPassword, String newPassword,
352 Message onComplete) {
353 if(mDbg) log("Change Pin2 old: " + oldPassword + " new: " + newPassword);
354 mPhone.mCM.changeIccPin2(oldPassword, newPassword,
355 mHandler.obtainMessage(EVENT_CHANGE_ICC_PASSWORD_DONE, onComplete));
361 * Returns service provider name stored in ICC card.
362 * If there is no service provider name associated or the record is not
363 * yet available, null will be returned <p>
365 * Please use this value when display Service Provider Name in idle mode <p>
367 * Usage of this provider name in the UI is a common carrier requirement.
369 * Also available via Android property "gsm.sim.operator.alpha"
371 * @return Service Provider Name stored in ICC card
372 * null if no service provider name associated or the record is not
376 public abstract String getServiceProviderName();
378 protected void updateStateProperty() {
379 mPhone.setSystemProperty(TelephonyProperties.PROPERTY_SIM_STATE, getState().toString());
382 private void getIccCardStatusDone(AsyncResult ar) {
383 if (ar.exception != null) {
384 Log.e(mLogTag,"Error getting ICC status. "
385 + "RIL_REQUEST_GET_ICC_STATUS should "
386 + "never return an error", ar.exception);
389 handleIccCardStatus((IccCardStatus) ar.result);
392 private void handleIccCardStatus(IccCardStatus newCardStatus) {
393 boolean transitionedIntoPinLocked;
394 boolean transitionedIntoAbsent;
395 boolean transitionedIntoNetworkLocked;
397 State oldState, newState;
400 mIccCardStatus = newCardStatus;
401 newState = getIccCardState();
404 updateStateProperty();
406 transitionedIntoPinLocked = (
407 (oldState != State.PIN_REQUIRED && newState == State.PIN_REQUIRED)
408 || (oldState != State.PUK_REQUIRED && newState == State.PUK_REQUIRED));
409 transitionedIntoAbsent = (oldState != State.ABSENT && newState == State.ABSENT);
410 transitionedIntoNetworkLocked = (oldState != State.NETWORK_LOCKED
411 && newState == State.NETWORK_LOCKED);
413 if (transitionedIntoPinLocked) {
414 if(mDbg) log("Notify SIM pin or puk locked.");
415 mPinLockedRegistrants.notifyRegistrants();
416 broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED,
417 (newState == State.PIN_REQUIRED) ?
418 INTENT_VALUE_LOCKED_ON_PIN : INTENT_VALUE_LOCKED_ON_PUK);
419 } else if (transitionedIntoAbsent) {
420 if(mDbg) log("Notify SIM missing.");
421 mAbsentRegistrants.notifyRegistrants();
422 broadcastIccStateChangedIntent(INTENT_VALUE_ICC_ABSENT, null);
423 } else if (transitionedIntoNetworkLocked) {
424 if(mDbg) log("Notify SIM network locked.");
425 mNetworkLockedRegistrants.notifyRegistrants();
426 broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED,
427 INTENT_VALUE_LOCKED_NETWORK);
432 * Interperate EVENT_QUERY_FACILITY_LOCK_DONE
433 * @param ar is asyncResult of Query_Facility_Locked
435 private void onQueryFdnEnabled(AsyncResult ar) {
436 if(ar.exception != null) {
437 if(mDbg) log("Error in querying facility lock:" + ar.exception);
441 int[] ints = (int[])ar.result;
442 if(ints.length != 0) {
443 mIccFdnEnabled = (0!=ints[0]);
444 if(mDbg) log("Query facility lock : " + mIccFdnEnabled);
446 Log.e(mLogTag, "[IccCard] Bogus facility lock response");
451 * Interperate EVENT_QUERY_FACILITY_LOCK_DONE
452 * @param ar is asyncResult of Query_Facility_Locked
454 private void onQueryFacilityLock(AsyncResult ar) {
455 if(ar.exception != null) {
456 if (mDbg) log("Error in querying facility lock:" + ar.exception);
460 int[] ints = (int[])ar.result;
461 if(ints.length != 0) {
462 mIccPinLocked = (0!=ints[0]);
463 if(mDbg) log("Query facility lock : " + mIccPinLocked);
465 Log.e(mLogTag, "[IccCard] Bogus facility lock response");
469 public void broadcastIccStateChangedIntent(String value, String reason) {
470 Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
471 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
472 intent.putExtra(Phone.PHONE_NAME_KEY, mPhone.getPhoneName());
473 intent.putExtra(INTENT_KEY_ICC_STATE, value);
474 intent.putExtra(INTENT_KEY_LOCKED_REASON, reason);
475 if(mDbg) log("Broadcasting intent ACTION_SIM_STATE_CHANGED " + value
476 + " reason " + reason);
477 ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE);
480 protected Handler mHandler = new Handler() {
482 public void handleMessage(Message msg){
486 serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
487 CommandsInterface.SERVICE_CLASS_DATA +
488 CommandsInterface.SERVICE_CLASS_FAX;
491 case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
493 updateStateProperty();
494 broadcastIccStateChangedIntent(INTENT_VALUE_ICC_NOT_READY, null);
496 case EVENT_ICC_READY:
497 //TODO: put facility read in SIM_READY now, maybe in REG_NW
498 mPhone.mCM.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
499 mPhone.mCM.queryFacilityLock (
500 CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX,
501 obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE));
502 mPhone.mCM.queryFacilityLock (
503 CommandsInterface.CB_FACILITY_BA_FD, "", serviceClassX,
504 obtainMessage(EVENT_QUERY_FACILITY_FDN_DONE));
506 case EVENT_ICC_LOCKED_OR_ABSENT:
507 mPhone.mCM.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
508 mPhone.mCM.queryFacilityLock (
509 CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX,
510 obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE));
512 case EVENT_GET_ICC_STATUS_DONE:
513 ar = (AsyncResult)msg.obj;
515 getIccCardStatusDone(ar);
517 case EVENT_PINPUK_DONE:
518 // a PIN/PUK/PIN2/PUK2/Network Personalization
519 // request has completed. ar.userObj is the response Message
520 // Repoll before returning
521 ar = (AsyncResult)msg.obj;
522 // TODO should abstract these exceptions
523 AsyncResult.forMessage(((Message)ar.userObj)).exception
525 mPhone.mCM.getIccCardStatus(
526 obtainMessage(EVENT_REPOLL_STATUS_DONE, ar.userObj));
528 case EVENT_REPOLL_STATUS_DONE:
529 // Finished repolling status after PIN operation
530 // ar.userObj is the response messaeg
531 // ar.userObj.obj is already an AsyncResult with an
532 // appropriate exception filled in if applicable
534 ar = (AsyncResult)msg.obj;
535 getIccCardStatusDone(ar);
536 ((Message)ar.userObj).sendToTarget();
538 case EVENT_QUERY_FACILITY_LOCK_DONE:
539 ar = (AsyncResult)msg.obj;
540 onQueryFacilityLock(ar);
542 case EVENT_QUERY_FACILITY_FDN_DONE:
543 ar = (AsyncResult)msg.obj;
544 onQueryFdnEnabled(ar);
546 case EVENT_CHANGE_FACILITY_LOCK_DONE:
547 ar = (AsyncResult)msg.obj;
548 if (ar.exception == null) {
549 mIccPinLocked = mDesiredPinLocked;
550 if (mDbg) log( "EVENT_CHANGE_FACILITY_LOCK_DONE: " +
551 "mIccPinLocked= " + mIccPinLocked);
553 Log.e(mLogTag, "Error change facility lock with exception "
556 AsyncResult.forMessage(((Message)ar.userObj)).exception
558 ((Message)ar.userObj).sendToTarget();
560 case EVENT_CHANGE_FACILITY_FDN_DONE:
561 ar = (AsyncResult)msg.obj;
563 if (ar.exception == null) {
564 mIccFdnEnabled = mDesiredFdnEnabled;
565 if (mDbg) log("EVENT_CHANGE_FACILITY_FDN_DONE: " +
566 "mIccFdnEnabled=" + mIccFdnEnabled);
568 Log.e(mLogTag, "Error change facility fdn with exception "
571 AsyncResult.forMessage(((Message)ar.userObj)).exception
573 ((Message)ar.userObj).sendToTarget();
575 case EVENT_CHANGE_ICC_PASSWORD_DONE:
576 ar = (AsyncResult)msg.obj;
577 if(ar.exception != null) {
578 Log.e(mLogTag, "Error in change sim password with exception"
581 AsyncResult.forMessage(((Message)ar.userObj)).exception
583 ((Message)ar.userObj).sendToTarget();
586 Log.e(mLogTag, "[IccCard] Unknown Event " + msg.what);
591 public State getIccCardState() {
592 if (mIccCardStatus == null) {
593 Log.e(mLogTag, "[IccCard] IccCardStatus is null");
594 return IccCard.State.ABSENT;
597 // this is common for all radio technologies
598 if (!mIccCardStatus.getCardState().isCardPresent()) {
599 return IccCard.State.ABSENT;
602 RadioState currentRadioState = mPhone.mCM.getRadioState();
603 // check radio technology
604 if( currentRadioState == RadioState.RADIO_OFF ||
605 currentRadioState == RadioState.RADIO_UNAVAILABLE ||
606 currentRadioState == RadioState.SIM_NOT_READY ||
607 currentRadioState == RadioState.RUIM_NOT_READY ||
608 currentRadioState == RadioState.NV_NOT_READY ||
609 currentRadioState == RadioState.NV_READY) {
610 return IccCard.State.NOT_READY;
613 if( currentRadioState == RadioState.SIM_LOCKED_OR_ABSENT ||
614 currentRadioState == RadioState.SIM_READY ||
615 currentRadioState == RadioState.RUIM_LOCKED_OR_ABSENT ||
616 currentRadioState == RadioState.RUIM_READY) {
620 // check for CDMA radio technology
621 if (currentRadioState == RadioState.RUIM_LOCKED_OR_ABSENT ||
622 currentRadioState == RadioState.RUIM_READY) {
623 index = mIccCardStatus.getCdmaSubscriptionAppIndex();
626 index = mIccCardStatus.getGsmUmtsSubscriptionAppIndex();
629 IccCardApplication app = mIccCardStatus.getApplication(index);
632 Log.e(mLogTag, "[IccCard] Subscription Application in not present");
633 return IccCard.State.ABSENT;
636 // check if PIN required
637 if (app.app_state.isPinRequired()) {
638 return IccCard.State.PIN_REQUIRED;
640 if (app.app_state.isPukRequired()) {
641 return IccCard.State.PUK_REQUIRED;
643 if (app.app_state.isSubscriptionPersoEnabled()) {
644 return IccCard.State.NETWORK_LOCKED;
646 if (app.app_state.isAppReady()) {
647 return IccCard.State.READY;
649 if (app.app_state.isAppNotReady()) {
650 return IccCard.State.NOT_READY;
652 return IccCard.State.NOT_READY;
655 return IccCard.State.ABSENT;
659 public boolean isApplicationOnIcc(IccCardApplication.AppType type) {
660 if (mIccCardStatus == null) return false;
662 for (int i = 0 ; i < mIccCardStatus.getNumApplications(); i++) {
663 IccCardApplication app = mIccCardStatus.getApplication(i);
664 if (app != null && app.app_type == type) {
672 * @return true if a ICC card is present
674 public boolean hasIccCard() {
675 boolean isIccPresent;
676 if (mPhone.getPhoneName().equals("GSM")) {
677 return mIccCardStatus.getCardState().isCardPresent();
679 // TODO: Make work with a CDMA device with a RUIM card.
684 private void log(String msg) {
685 Log.d(mLogTag, "[IccCard] " + msg);