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.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