Home | History | Annotate | Download | only in telephony
      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.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;
     28 
     29 import com.android.internal.telephony.PhoneBase;
     30 import com.android.internal.telephony.CommandsInterface.RadioState;
     31 
     32 /**
     33  * {@hide}
     34  */
     35 public abstract class IccCard {
     36     protected String mLogTag;
     37     protected boolean mDbg;
     38 
     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();
     45 
     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.
     51 
     52 
     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";
     75 
     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;
     87 
     88     /*
     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
     91       turns to READY
     92      */
     93     public enum State {
     94         UNKNOWN,
     95         ABSENT,
     96         PIN_REQUIRED,
     97         PUK_REQUIRED,
     98         NETWORK_LOCKED,
     99         READY,
    100         NOT_READY;
    101 
    102         public boolean isPinLocked() {
    103             return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED));
    104         }
    105     }
    106 
    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
    113                  */
    114                 case RADIO_OFF:
    115                 case RADIO_UNAVAILABLE:
    116                 case SIM_NOT_READY:
    117                 case RUIM_NOT_READY:
    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;
    123                 case SIM_READY:
    124                 case RUIM_READY:
    125                 case NV_READY:
    126                     return State.READY;
    127                 case NV_NOT_READY:
    128                     return State.ABSENT;
    129             }
    130         } else {
    131             return mState;
    132         }
    133 
    134         Log.e(mLogTag, "IccCard.getState(): case should never be reached");
    135         return State.UNKNOWN;
    136     }
    137 
    138     public IccCard(PhoneBase phone, String logTag, Boolean dbg) {
    139         mPhone = phone;
    140         mLogTag = logTag;
    141         mDbg = dbg;
    142     }
    143 
    144     abstract public void dispose();
    145 
    146     protected void finalize() {
    147         if(mDbg) Log.d(mLogTag, "IccCard finalized");
    148     }
    149 
    150     /**
    151      * Notifies handler of any transition into State.ABSENT
    152      */
    153     public void registerForAbsent(Handler h, int what, Object obj) {
    154         Registrant r = new Registrant (h, what, obj);
    155 
    156         mAbsentRegistrants.add(r);
    157 
    158         if (getState() == State.ABSENT) {
    159             r.notifyRegistrant();
    160         }
    161     }
    162 
    163     public void unregisterForAbsent(Handler h) {
    164         mAbsentRegistrants.remove(h);
    165     }
    166 
    167     /**
    168      * Notifies handler of any transition into State.NETWORK_LOCKED
    169      */
    170     public void registerForNetworkLocked(Handler h, int what, Object obj) {
    171         Registrant r = new Registrant (h, what, obj);
    172 
    173         mNetworkLockedRegistrants.add(r);
    174 
    175         if (getState() == State.NETWORK_LOCKED) {
    176             r.notifyRegistrant();
    177         }
    178     }
    179 
    180     public void unregisterForNetworkLocked(Handler h) {
    181         mNetworkLockedRegistrants.remove(h);
    182     }
    183 
    184     /**
    185      * Notifies handler of any transition into State.isPinLocked()
    186      */
    187     public void registerForLocked(Handler h, int what, Object obj) {
    188         Registrant r = new Registrant (h, what, obj);
    189 
    190         mPinLockedRegistrants.add(r);
    191 
    192         if (getState().isPinLocked()) {
    193             r.notifyRegistrant();
    194         }
    195     }
    196 
    197     public void unregisterForLocked(Handler h) {
    198         mPinLockedRegistrants.remove(h);
    199     }
    200 
    201 
    202     /**
    203      * Supply the ICC PIN to the ICC
    204      *
    205      * When the operation is complete, onComplete will be sent to its
    206      * Handler.
    207      *
    208      * onComplete.obj will be an AsyncResult
    209      *
    210      * ((AsyncResult)onComplete.obj).exception == null on success
    211      * ((AsyncResult)onComplete.obj).exception != null on fail
    212      *
    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
    219      *
    220      *
    221      */
    222 
    223     public void supplyPin (String pin, Message onComplete) {
    224         mPhone.mCM.supplyIccPin(pin, mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
    225     }
    226 
    227     public void supplyPuk (String puk, String newPin, Message onComplete) {
    228         mPhone.mCM.supplyIccPuk(puk, newPin,
    229                 mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
    230     }
    231 
    232     public void supplyPin2 (String pin2, Message onComplete) {
    233         mPhone.mCM.supplyIccPin2(pin2,
    234                 mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
    235     }
    236 
    237     public void supplyPuk2 (String puk2, String newPin2, Message onComplete) {
    238         mPhone.mCM.supplyIccPuk2(puk2, newPin2,
    239                 mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
    240     }
    241 
    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));
    246     }
    247 
    248     /**
    249      * Check whether ICC pin lock is enabled
    250      * This is a sync call which returns the cached pin enabled state
    251      *
    252      * @return true for ICC locked enabled
    253      *         false for ICC locked disabled
    254      */
    255     public boolean getIccLockEnabled() {
    256         return mIccPinLocked;
    257      }
    258 
    259     /**
    260      * Check whether ICC fdn (fixed dialing number) is enabled
    261      * This is a sync call which returns the cached pin enabled state
    262      *
    263      * @return true for ICC fdn enabled
    264      *         false for ICC fdn disabled
    265      */
    266      public boolean getIccFdnEnabled() {
    267         return mIccFdnEnabled;
    268      }
    269 
    270      /**
    271       * Set the ICC pin lock enabled or disabled
    272       * When the operation is complete, onComplete will be sent to its handler
    273       *
    274       * @param enabled "true" for locked "false" for unlocked.
    275       * @param password needed to change the ICC pin state, aka. Pin1
    276       * @param onComplete
    277       *        onComplete.obj will be an AsyncResult
    278       *        ((AsyncResult)onComplete.obj).exception == null on success
    279       *        ((AsyncResult)onComplete.obj).exception != null on fail
    280       */
    281      public void setIccLockEnabled (boolean enabled,
    282              String password, Message onComplete) {
    283          int serviceClassX;
    284          serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
    285                  CommandsInterface.SERVICE_CLASS_DATA +
    286                  CommandsInterface.SERVICE_CLASS_FAX;
    287 
    288          mDesiredPinLocked = enabled;
    289 
    290          mPhone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_SIM,
    291                  enabled, password, serviceClassX,
    292                  mHandler.obtainMessage(EVENT_CHANGE_FACILITY_LOCK_DONE, onComplete));
    293      }
    294 
    295      /**
    296       * Set the ICC fdn enabled or disabled
    297       * When the operation is complete, onComplete will be sent to its handler
    298       *
    299       * @param enabled "true" for locked "false" for unlocked.
    300       * @param password needed to change the ICC fdn enable, aka Pin2
    301       * @param onComplete
    302       *        onComplete.obj will be an AsyncResult
    303       *        ((AsyncResult)onComplete.obj).exception == null on success
    304       *        ((AsyncResult)onComplete.obj).exception != null on fail
    305       */
    306      public void setIccFdnEnabled (boolean enabled,
    307              String password, Message onComplete) {
    308          int serviceClassX;
    309          serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
    310                  CommandsInterface.SERVICE_CLASS_DATA +
    311                  CommandsInterface.SERVICE_CLASS_FAX +
    312                  CommandsInterface.SERVICE_CLASS_SMS;
    313 
    314          mDesiredFdnEnabled = enabled;
    315 
    316          mPhone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_FD,
    317                  enabled, password, serviceClassX,
    318                  mHandler.obtainMessage(EVENT_CHANGE_FACILITY_FDN_DONE, onComplete));
    319      }
    320 
    321      /**
    322       * Change the ICC password used in ICC pin lock
    323       * When the operation is complete, onComplete will be sent to its handler
    324       *
    325       * @param oldPassword is the old password
    326       * @param newPassword is the new password
    327       * @param onComplete
    328       *        onComplete.obj will be an AsyncResult
    329       *        ((AsyncResult)onComplete.obj).exception == null on success
    330       *        ((AsyncResult)onComplete.obj).exception != null on fail
    331       */
    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));
    337 
    338      }
    339 
    340      /**
    341       * Change the ICC password used in ICC fdn enable
    342       * When the operation is complete, onComplete will be sent to its handler
    343       *
    344       * @param oldPassword is the old password
    345       * @param newPassword is the new password
    346       * @param onComplete
    347       *        onComplete.obj will be an AsyncResult
    348       *        ((AsyncResult)onComplete.obj).exception == null on success
    349       *        ((AsyncResult)onComplete.obj).exception != null on fail
    350       */
    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));
    356 
    357      }
    358 
    359 
    360     /**
    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>
    364      *
    365      * Please use this value when display Service Provider Name in idle mode <p>
    366      *
    367      * Usage of this provider name in the UI is a common carrier requirement.
    368      *
    369      * Also available via Android property "gsm.sim.operator.alpha"
    370      *
    371      * @return Service Provider Name stored in ICC card
    372      *         null if no service provider name associated or the record is not
    373      *         yet available
    374      *
    375      */
    376     public abstract String getServiceProviderName();
    377 
    378     protected void updateStateProperty() {
    379         mPhone.setSystemProperty(TelephonyProperties.PROPERTY_SIM_STATE, getState().toString());
    380     }
    381 
    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);
    387             return;
    388         }
    389         handleIccCardStatus((IccCardStatus) ar.result);
    390     }
    391 
    392     private void handleIccCardStatus(IccCardStatus newCardStatus) {
    393         boolean transitionedIntoPinLocked;
    394         boolean transitionedIntoAbsent;
    395         boolean transitionedIntoNetworkLocked;
    396 
    397         State oldState, newState;
    398 
    399         oldState = mState;
    400         mIccCardStatus = newCardStatus;
    401         newState = getIccCardState();
    402         mState = newState;
    403 
    404         updateStateProperty();
    405 
    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);
    412 
    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);
    428         }
    429     }
    430 
    431     /**
    432      * Interperate EVENT_QUERY_FACILITY_LOCK_DONE
    433      * @param ar is asyncResult of Query_Facility_Locked
    434      */
    435     private void onQueryFdnEnabled(AsyncResult ar) {
    436         if(ar.exception != null) {
    437             if(mDbg) log("Error in querying facility lock:" + ar.exception);
    438             return;
    439         }
    440 
    441         int[] ints = (int[])ar.result;
    442         if(ints.length != 0) {
    443             mIccFdnEnabled = (0!=ints[0]);
    444             if(mDbg) log("Query facility lock : "  + mIccFdnEnabled);
    445         } else {
    446             Log.e(mLogTag, "[IccCard] Bogus facility lock response");
    447         }
    448     }
    449 
    450     /**
    451      * Interperate EVENT_QUERY_FACILITY_LOCK_DONE
    452      * @param ar is asyncResult of Query_Facility_Locked
    453      */
    454     private void onQueryFacilityLock(AsyncResult ar) {
    455         if(ar.exception != null) {
    456             if (mDbg) log("Error in querying facility lock:" + ar.exception);
    457             return;
    458         }
    459 
    460         int[] ints = (int[])ar.result;
    461         if(ints.length != 0) {
    462             mIccPinLocked = (0!=ints[0]);
    463             if(mDbg) log("Query facility lock : "  + mIccPinLocked);
    464         } else {
    465             Log.e(mLogTag, "[IccCard] Bogus facility lock response");
    466         }
    467     }
    468 
    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);
    478     }
    479 
    480     protected Handler mHandler = new Handler() {
    481         @Override
    482         public void handleMessage(Message msg){
    483             AsyncResult ar;
    484             int serviceClassX;
    485 
    486             serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
    487                             CommandsInterface.SERVICE_CLASS_DATA +
    488                             CommandsInterface.SERVICE_CLASS_FAX;
    489 
    490             switch (msg.what) {
    491                 case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
    492                     mState = null;
    493                     updateStateProperty();
    494                     broadcastIccStateChangedIntent(INTENT_VALUE_ICC_NOT_READY, null);
    495                     break;
    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));
    505                     break;
    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));
    511                     break;
    512                 case EVENT_GET_ICC_STATUS_DONE:
    513                     ar = (AsyncResult)msg.obj;
    514 
    515                     getIccCardStatusDone(ar);
    516                     break;
    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
    524                                                         = ar.exception;
    525                     mPhone.mCM.getIccCardStatus(
    526                         obtainMessage(EVENT_REPOLL_STATUS_DONE, ar.userObj));
    527                     break;
    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
    533 
    534                     ar = (AsyncResult)msg.obj;
    535                     getIccCardStatusDone(ar);
    536                     ((Message)ar.userObj).sendToTarget();
    537                     break;
    538                 case EVENT_QUERY_FACILITY_LOCK_DONE:
    539                     ar = (AsyncResult)msg.obj;
    540                     onQueryFacilityLock(ar);
    541                     break;
    542                 case EVENT_QUERY_FACILITY_FDN_DONE:
    543                     ar = (AsyncResult)msg.obj;
    544                     onQueryFdnEnabled(ar);
    545                     break;
    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);
    552                     } else {
    553                         Log.e(mLogTag, "Error change facility lock with exception "
    554                             + ar.exception);
    555                     }
    556                     AsyncResult.forMessage(((Message)ar.userObj)).exception
    557                                                         = ar.exception;
    558                     ((Message)ar.userObj).sendToTarget();
    559                     break;
    560                 case EVENT_CHANGE_FACILITY_FDN_DONE:
    561                     ar = (AsyncResult)msg.obj;
    562 
    563                     if (ar.exception == null) {
    564                         mIccFdnEnabled = mDesiredFdnEnabled;
    565                         if (mDbg) log("EVENT_CHANGE_FACILITY_FDN_DONE: " +
    566                                 "mIccFdnEnabled=" + mIccFdnEnabled);
    567                     } else {
    568                         Log.e(mLogTag, "Error change facility fdn with exception "
    569                                 + ar.exception);
    570                     }
    571                     AsyncResult.forMessage(((Message)ar.userObj)).exception
    572                                                         = ar.exception;
    573                     ((Message)ar.userObj).sendToTarget();
    574                     break;
    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"
    579                             + ar.exception);
    580                     }
    581                     AsyncResult.forMessage(((Message)ar.userObj)).exception
    582                                                         = ar.exception;
    583                     ((Message)ar.userObj).sendToTarget();
    584                     break;
    585                 default:
    586                     Log.e(mLogTag, "[IccCard] Unknown Event " + msg.what);
    587             }
    588         }
    589     };
    590 
    591     public State getIccCardState() {
    592         if (mIccCardStatus == null) {
    593             Log.e(mLogTag, "[IccCard] IccCardStatus is null");
    594             return IccCard.State.ABSENT;
    595         }
    596 
    597         // this is common for all radio technologies
    598         if (!mIccCardStatus.getCardState().isCardPresent()) {
    599             return IccCard.State.ABSENT;
    600         }
    601 
    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;
    611         }
    612 
    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) {
    617 
    618             int index;
    619 
    620             // check for CDMA radio technology
    621             if (currentRadioState == RadioState.RUIM_LOCKED_OR_ABSENT ||
    622                 currentRadioState == RadioState.RUIM_READY) {
    623                 index = mIccCardStatus.getCdmaSubscriptionAppIndex();
    624             }
    625             else {
    626                 index = mIccCardStatus.getGsmUmtsSubscriptionAppIndex();
    627             }
    628 
    629             IccCardApplication app = mIccCardStatus.getApplication(index);
    630 
    631             if (app == null) {
    632                 Log.e(mLogTag, "[IccCard] Subscription Application in not present");
    633                 return IccCard.State.ABSENT;
    634             }
    635 
    636             // check if PIN required
    637             if (app.app_state.isPinRequired()) {
    638                 return IccCard.State.PIN_REQUIRED;
    639             }
    640             if (app.app_state.isPukRequired()) {
    641                 return IccCard.State.PUK_REQUIRED;
    642             }
    643             if (app.app_state.isSubscriptionPersoEnabled()) {
    644                 return IccCard.State.NETWORK_LOCKED;
    645             }
    646             if (app.app_state.isAppReady()) {
    647                 return IccCard.State.READY;
    648             }
    649             if (app.app_state.isAppNotReady()) {
    650                 return IccCard.State.NOT_READY;
    651             }
    652             return IccCard.State.NOT_READY;
    653         }
    654 
    655         return IccCard.State.ABSENT;
    656     }
    657 
    658 
    659     public boolean isApplicationOnIcc(IccCardApplication.AppType type) {
    660         if (mIccCardStatus == null) return false;
    661 
    662         for (int i = 0 ; i < mIccCardStatus.getNumApplications(); i++) {
    663             IccCardApplication app = mIccCardStatus.getApplication(i);
    664             if (app != null && app.app_type == type) {
    665                 return true;
    666             }
    667         }
    668         return false;
    669     }
    670 
    671     /**
    672      * @return true if a ICC card is present
    673      */
    674     public boolean hasIccCard() {
    675         boolean isIccPresent;
    676         if (mPhone.getPhoneName().equals("GSM")) {
    677             return mIccCardStatus.getCardState().isCardPresent();
    678         } else {
    679             // TODO: Make work with a CDMA device with a RUIM card.
    680             return false;
    681         }
    682     }
    683 
    684     private void log(String msg) {
    685         Log.d(mLogTag, "[IccCard] " + msg);
    686     }
    687 }
    688