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