Home | History | Annotate | Download | only in uicc
      1 /*
      2  * Copyright (C) 2012 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.uicc;
     18 
     19 import static android.Manifest.permission.READ_PHONE_STATE;
     20 
     21 import android.app.ActivityManagerNative;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.os.AsyncResult;
     25 import android.os.Handler;
     26 import android.os.Message;
     27 import android.os.Registrant;
     28 import android.os.RegistrantList;
     29 import android.os.SystemProperties;
     30 import android.os.UserHandle;
     31 import android.telephony.Rlog;
     32 import android.telephony.ServiceState;
     33 import android.telephony.SubscriptionManager;
     34 import android.telephony.TelephonyManager;
     35 
     36 import com.android.internal.telephony.CommandsInterface;
     37 import com.android.internal.telephony.IccCard;
     38 import com.android.internal.telephony.IccCardConstants;
     39 import com.android.internal.telephony.PhoneConstants;
     40 import com.android.internal.telephony.MccTable;
     41 import com.android.internal.telephony.RILConstants;
     42 import com.android.internal.telephony.TelephonyIntents;
     43 import com.android.internal.telephony.IccCardConstants.State;
     44 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
     45 import com.android.internal.telephony.Phone;
     46 import com.android.internal.telephony.SubscriptionController;
     47 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
     48 import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
     49 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
     50 import com.android.internal.telephony.uicc.IccCardStatus.PinState;
     51 import com.android.internal.telephony.uicc.UiccController;
     52 
     53 import java.io.FileDescriptor;
     54 import java.io.PrintWriter;
     55 
     56 /**
     57  * @Deprecated use {@link UiccController}.getUiccCard instead.
     58  *
     59  * The Phone App assumes that there is only one icc card, and one icc application
     60  * available at a time. Moreover, it assumes such object (represented with IccCard)
     61  * is available all the time (whether {@link RILConstants#RIL_REQUEST_GET_SIM_STATUS} returned
     62  * or not, whether card has desired application or not, whether there really is a card in the
     63  * slot or not).
     64  *
     65  * UiccController, however, can handle multiple instances of icc objects (multiple
     66  * {@link UiccCardApplication}, multiple {@link IccFileHandler}, multiple {@link IccRecords})
     67  * created and destroyed dynamically during phone operation.
     68  *
     69  * This class implements the IccCard interface that is always available (right after default
     70  * phone object is constructed) to expose the current (based on voice radio technology)
     71  * application on the uicc card, so that external apps won't break.
     72  */
     73 
     74 public class IccCardProxy extends Handler implements IccCard {
     75     private static final boolean DBG = true;
     76     private static final String LOG_TAG = "IccCardProxy";
     77 
     78     private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 1;
     79     private static final int EVENT_RADIO_ON = 2;
     80     private static final int EVENT_ICC_CHANGED = 3;
     81     private static final int EVENT_ICC_ABSENT = 4;
     82     private static final int EVENT_ICC_LOCKED = 5;
     83     private static final int EVENT_APP_READY = 6;
     84     private static final int EVENT_RECORDS_LOADED = 7;
     85     private static final int EVENT_IMSI_READY = 8;
     86     private static final int EVENT_NETWORK_LOCKED = 9;
     87     private static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = 11;
     88 
     89     private static final int EVENT_ICC_RECORD_EVENTS = 500;
     90     private static final int EVENT_SUBSCRIPTION_ACTIVATED = 501;
     91     private static final int EVENT_SUBSCRIPTION_DEACTIVATED = 502;
     92     private static final int EVENT_CARRIER_PRIVILIGES_LOADED = 503;
     93 
     94     private Integer mPhoneId = null;
     95 
     96     private final Object mLock = new Object();
     97     private Context mContext;
     98     private CommandsInterface mCi;
     99     private TelephonyManager mTelephonyManager;
    100 
    101     private RegistrantList mAbsentRegistrants = new RegistrantList();
    102     private RegistrantList mPinLockedRegistrants = new RegistrantList();
    103     private RegistrantList mNetworkLockedRegistrants = new RegistrantList();
    104 
    105     private int mCurrentAppType = UiccController.APP_FAM_3GPP; //default to 3gpp?
    106     private UiccController mUiccController = null;
    107     private UiccCard mUiccCard = null;
    108     private UiccCardApplication mUiccApplication = null;
    109     private IccRecords mIccRecords = null;
    110     private CdmaSubscriptionSourceManager mCdmaSSM = null;
    111     private boolean mRadioOn = false;
    112     private boolean mQuietMode = false; // when set to true IccCardProxy will not broadcast
    113                                         // ACTION_SIM_STATE_CHANGED intents
    114     private boolean mInitialized = false;
    115     private State mExternalState = State.UNKNOWN;
    116 
    117     public static final String ACTION_INTERNAL_SIM_STATE_CHANGED = "android.intent.action.internal_sim_state_changed";
    118 
    119     public IccCardProxy(Context context, CommandsInterface ci, int phoneId) {
    120         if (DBG) log("ctor: ci=" + ci + " phoneId=" + phoneId);
    121         mContext = context;
    122         mCi = ci;
    123         mPhoneId = phoneId;
    124         mTelephonyManager = (TelephonyManager) mContext.getSystemService(
    125                 Context.TELEPHONY_SERVICE);
    126         mCdmaSSM = CdmaSubscriptionSourceManager.getInstance(context,
    127                 ci, this, EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null);
    128         mUiccController = UiccController.getInstance();
    129         mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
    130         ci.registerForOn(this,EVENT_RADIO_ON, null);
    131         ci.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_UNAVAILABLE, null);
    132 
    133         resetProperties();
    134         setExternalState(State.NOT_READY, false);
    135     }
    136 
    137     public void dispose() {
    138         synchronized (mLock) {
    139             log("Disposing");
    140             //Cleanup icc references
    141             mUiccController.unregisterForIccChanged(this);
    142             mUiccController = null;
    143             mCi.unregisterForOn(this);
    144             mCi.unregisterForOffOrNotAvailable(this);
    145             mCdmaSSM.dispose(this);
    146         }
    147     }
    148 
    149     /*
    150      * The card application that the external world sees will be based on the
    151      * voice radio technology only!
    152      */
    153     public void setVoiceRadioTech(int radioTech) {
    154         synchronized (mLock) {
    155             if (DBG) {
    156                 log("Setting radio tech " + ServiceState.rilRadioTechnologyToString(radioTech));
    157             }
    158             if (ServiceState.isGsm(radioTech)) {
    159                 mCurrentAppType = UiccController.APP_FAM_3GPP;
    160             } else {
    161                 mCurrentAppType = UiccController.APP_FAM_3GPP2;
    162             }
    163             updateQuietMode();
    164         }
    165     }
    166 
    167     /**
    168      * In case of 3gpp2 we need to find out if subscription used is coming from
    169      * NV in which case we shouldn't broadcast any sim states changes.
    170      */
    171     private void updateQuietMode() {
    172         synchronized (mLock) {
    173             boolean oldQuietMode = mQuietMode;
    174             boolean newQuietMode;
    175             int cdmaSource = Phone.CDMA_SUBSCRIPTION_UNKNOWN;
    176             boolean isLteOnCdmaMode = TelephonyManager.getLteOnCdmaModeStatic()
    177                     == PhoneConstants.LTE_ON_CDMA_TRUE;
    178             if (mCurrentAppType == UiccController.APP_FAM_3GPP) {
    179                 newQuietMode = false;
    180                 if (DBG) log("updateQuietMode: 3GPP subscription -> newQuietMode=" + newQuietMode);
    181             } else {
    182                 if (isLteOnCdmaMode) {
    183                     log("updateQuietMode: is cdma/lte device, force IccCardProxy into 3gpp mode");
    184                     mCurrentAppType = UiccController.APP_FAM_3GPP;
    185                 }
    186                 cdmaSource = mCdmaSSM != null ?
    187                         mCdmaSSM.getCdmaSubscriptionSource() : Phone.CDMA_SUBSCRIPTION_UNKNOWN;
    188 
    189                 newQuietMode = (cdmaSource == Phone.CDMA_SUBSCRIPTION_NV)
    190                         && (mCurrentAppType == UiccController.APP_FAM_3GPP2)
    191                         && !isLteOnCdmaMode;
    192                 if (DBG) {
    193                     log("updateQuietMode: cdmaSource=" + cdmaSource
    194                             + " mCurrentAppType=" + mCurrentAppType
    195                             + " isLteOnCdmaMode=" + isLteOnCdmaMode
    196                             + " newQuietMode=" + newQuietMode);
    197                 }
    198             }
    199 
    200             if (mQuietMode == false && newQuietMode == true) {
    201                 // Last thing to do before switching to quiet mode is
    202                 // broadcast ICC_READY
    203                 log("Switching to QuietMode.");
    204                 setExternalState(State.READY);
    205                 mQuietMode = newQuietMode;
    206             } else if (mQuietMode == true && newQuietMode == false) {
    207                 if (DBG) {
    208                     log("updateQuietMode: Switching out from QuietMode."
    209                             + " Force broadcast of current state=" + mExternalState);
    210                 }
    211                 mQuietMode = newQuietMode;
    212                 setExternalState(mExternalState, true);
    213             } else {
    214                 if (DBG) log("updateQuietMode: no changes don't setExternalState");
    215             }
    216             if (DBG) {
    217                 log("updateQuietMode: QuietMode is " + mQuietMode + " (app_type="
    218                     + mCurrentAppType + " isLteOnCdmaMode=" + isLteOnCdmaMode
    219                     + " cdmaSource=" + cdmaSource + ")");
    220             }
    221             mInitialized = true;
    222             sendMessage(obtainMessage(EVENT_ICC_CHANGED));
    223         }
    224     }
    225 
    226     @Override
    227     public void handleMessage(Message msg) {
    228         switch (msg.what) {
    229             case EVENT_RADIO_OFF_OR_UNAVAILABLE:
    230                 mRadioOn = false;
    231                 if (CommandsInterface.RadioState.RADIO_UNAVAILABLE == mCi.getRadioState()) {
    232                     setExternalState(State.NOT_READY);
    233                 }
    234                 break;
    235             case EVENT_RADIO_ON:
    236                 mRadioOn = true;
    237                 if (!mInitialized) {
    238                     updateQuietMode();
    239                 }
    240                 break;
    241             case EVENT_ICC_CHANGED:
    242                 if (mInitialized) {
    243                     updateIccAvailability();
    244                 }
    245                 break;
    246             case EVENT_ICC_ABSENT:
    247                 mAbsentRegistrants.notifyRegistrants();
    248                 setExternalState(State.ABSENT);
    249                 break;
    250             case EVENT_ICC_LOCKED:
    251                 processLockedState();
    252                 break;
    253             case EVENT_APP_READY:
    254                 setExternalState(State.READY);
    255                 break;
    256             case EVENT_RECORDS_LOADED:
    257                 // Update the MCC/MNC.
    258                 if (mIccRecords != null) {
    259                     String operator = mIccRecords.getOperatorNumeric();
    260                     log("operator=" + operator + " mPhoneId=" + mPhoneId);
    261 
    262                     if (operator != null) {
    263                         log("update icc_operator_numeric=" + operator);
    264                         mTelephonyManager.setSimOperatorNumericForPhone(mPhoneId, operator);
    265                         String countryCode = operator.substring(0,3);
    266                         if (countryCode != null) {
    267                             mTelephonyManager.setSimCountryIsoForPhone(mPhoneId,
    268                                     MccTable.countryCodeForMcc(Integer.parseInt(countryCode)));
    269                         } else {
    270                             loge("EVENT_RECORDS_LOADED Country code is null");
    271                         }
    272                     } else {
    273                         loge("EVENT_RECORDS_LOADED Operator name is null");
    274                     }
    275                 }
    276                 if (mUiccCard != null && !mUiccCard.areCarrierPriviligeRulesLoaded()) {
    277                     mUiccCard.registerForCarrierPrivilegeRulesLoaded(
    278                         this, EVENT_CARRIER_PRIVILIGES_LOADED, null);
    279                 } else {
    280                     onRecordsLoaded();
    281                 }
    282                 break;
    283             case EVENT_IMSI_READY:
    284                 broadcastIccStateChangedIntent(IccCardConstants.INTENT_VALUE_ICC_IMSI, null);
    285                 break;
    286             case EVENT_NETWORK_LOCKED:
    287                 mNetworkLockedRegistrants.notifyRegistrants();
    288                 setExternalState(State.NETWORK_LOCKED);
    289                 break;
    290             case EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
    291                 updateQuietMode();
    292                 break;
    293             case EVENT_SUBSCRIPTION_ACTIVATED:
    294                 log("EVENT_SUBSCRIPTION_ACTIVATED");
    295                 onSubscriptionActivated();
    296                 break;
    297 
    298             case EVENT_SUBSCRIPTION_DEACTIVATED:
    299                 log("EVENT_SUBSCRIPTION_DEACTIVATED");
    300                 onSubscriptionDeactivated();
    301                 break;
    302 
    303             case EVENT_ICC_RECORD_EVENTS:
    304                 if ((mCurrentAppType == UiccController.APP_FAM_3GPP) && (mIccRecords != null)) {
    305                     AsyncResult ar = (AsyncResult)msg.obj;
    306                     int eventCode = (Integer) ar.result;
    307                     if (eventCode == SIMRecords.EVENT_SPN) {
    308                         mTelephonyManager.setSimOperatorNameForPhone(
    309                                 mPhoneId, mIccRecords.getServiceProviderName());
    310                     }
    311                 }
    312                 break;
    313 
    314             case EVENT_CARRIER_PRIVILIGES_LOADED:
    315                 log("EVENT_CARRIER_PRIVILEGES_LOADED");
    316                 if (mUiccCard != null) {
    317                     mUiccCard.unregisterForCarrierPrivilegeRulesLoaded(this);
    318                 }
    319                 onRecordsLoaded();
    320                 break;
    321 
    322             default:
    323                 loge("Unhandled message with number: " + msg.what);
    324                 break;
    325         }
    326     }
    327 
    328     private void onSubscriptionActivated() {
    329         updateIccAvailability();
    330         updateStateProperty();
    331     }
    332 
    333     private void onSubscriptionDeactivated() {
    334         resetProperties();
    335         updateIccAvailability();
    336         updateStateProperty();
    337     }
    338 
    339     private void onRecordsLoaded() {
    340         broadcastInternalIccStateChangedIntent(IccCardConstants.INTENT_VALUE_ICC_LOADED, null);
    341     }
    342 
    343     private void updateIccAvailability() {
    344         synchronized (mLock) {
    345             UiccCard newCard = mUiccController.getUiccCard(mPhoneId);
    346             CardState state = CardState.CARDSTATE_ABSENT;
    347             UiccCardApplication newApp = null;
    348             IccRecords newRecords = null;
    349             if (newCard != null) {
    350                 state = newCard.getCardState();
    351                 newApp = newCard.getApplication(mCurrentAppType);
    352                 if (newApp != null) {
    353                     newRecords = newApp.getIccRecords();
    354                 }
    355             }
    356 
    357             if (mIccRecords != newRecords || mUiccApplication != newApp || mUiccCard != newCard) {
    358                 if (DBG) log("Icc changed. Reregestering.");
    359                 unregisterUiccCardEvents();
    360                 mUiccCard = newCard;
    361                 mUiccApplication = newApp;
    362                 mIccRecords = newRecords;
    363                 registerUiccCardEvents();
    364             }
    365 
    366             updateExternalState();
    367         }
    368     }
    369 
    370     void resetProperties() {
    371         if (mCurrentAppType == UiccController.APP_FAM_3GPP) {
    372             log("update icc_operator_numeric=" + "");
    373             mTelephonyManager.setSimOperatorNumericForPhone(mPhoneId, "");
    374             mTelephonyManager.setSimCountryIsoForPhone(mPhoneId, "");
    375             mTelephonyManager.setSimOperatorNameForPhone(mPhoneId, "");
    376          }
    377     }
    378 
    379     private void HandleDetectedState() {
    380     // CAF_MSIM SAND
    381 //        setExternalState(State.DETECTED, false);
    382     }
    383 
    384     private void updateExternalState() {
    385         if (mUiccCard == null || mUiccCard.getCardState() == CardState.CARDSTATE_ABSENT) {
    386             if (mRadioOn) {
    387                 setExternalState(State.ABSENT);
    388             } else {
    389                 setExternalState(State.NOT_READY);
    390             }
    391             return;
    392         }
    393 
    394         if (mUiccCard.getCardState() == CardState.CARDSTATE_ERROR) {
    395             setExternalState(State.CARD_IO_ERROR);
    396             return;
    397         }
    398 
    399         if (mUiccApplication == null) {
    400             setExternalState(State.NOT_READY);
    401             return;
    402         }
    403 
    404         switch (mUiccApplication.getState()) {
    405             case APPSTATE_UNKNOWN:
    406                 setExternalState(State.UNKNOWN);
    407                 break;
    408             case APPSTATE_DETECTED:
    409                 HandleDetectedState();
    410                 break;
    411             case APPSTATE_PIN:
    412                 setExternalState(State.PIN_REQUIRED);
    413                 break;
    414             case APPSTATE_PUK:
    415                 setExternalState(State.PUK_REQUIRED);
    416                 break;
    417             case APPSTATE_SUBSCRIPTION_PERSO:
    418                 if (mUiccApplication.getPersoSubState() ==
    419                         PersoSubState.PERSOSUBSTATE_SIM_NETWORK) {
    420                     setExternalState(State.NETWORK_LOCKED);
    421                 } else {
    422                     setExternalState(State.UNKNOWN);
    423                 }
    424                 break;
    425             case APPSTATE_READY:
    426                 setExternalState(State.READY);
    427                 break;
    428         }
    429     }
    430 
    431     private void registerUiccCardEvents() {
    432         if (mUiccCard != null) {
    433             mUiccCard.registerForAbsent(this, EVENT_ICC_ABSENT, null);
    434         }
    435         if (mUiccApplication != null) {
    436             mUiccApplication.registerForReady(this, EVENT_APP_READY, null);
    437             mUiccApplication.registerForLocked(this, EVENT_ICC_LOCKED, null);
    438             mUiccApplication.registerForNetworkLocked(this, EVENT_NETWORK_LOCKED, null);
    439         }
    440         if (mIccRecords != null) {
    441             mIccRecords.registerForImsiReady(this, EVENT_IMSI_READY, null);
    442             mIccRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
    443             mIccRecords.registerForRecordsEvents(this, EVENT_ICC_RECORD_EVENTS, null);
    444         }
    445     }
    446 
    447     private void unregisterUiccCardEvents() {
    448         if (mUiccCard != null) mUiccCard.unregisterForAbsent(this);
    449         if (mUiccApplication != null) mUiccApplication.unregisterForReady(this);
    450         if (mUiccApplication != null) mUiccApplication.unregisterForLocked(this);
    451         if (mUiccApplication != null) mUiccApplication.unregisterForNetworkLocked(this);
    452         if (mIccRecords != null) mIccRecords.unregisterForImsiReady(this);
    453         if (mIccRecords != null) mIccRecords.unregisterForRecordsLoaded(this);
    454         if (mIccRecords != null) mIccRecords.unregisterForRecordsEvents(this);
    455     }
    456 
    457     private void updateStateProperty() {
    458         mTelephonyManager.setSimStateForPhone(mPhoneId, getState().toString());
    459     }
    460 
    461     private void broadcastIccStateChangedIntent(String value, String reason) {
    462         synchronized (mLock) {
    463             if (mPhoneId == null || !SubscriptionManager.isValidSlotId(mPhoneId)) {
    464                 loge("broadcastIccStateChangedIntent: mPhoneId=" + mPhoneId
    465                         + " is invalid; Return!!");
    466                 return;
    467             }
    468 
    469             if (mQuietMode) {
    470                 log("broadcastIccStateChangedIntent: QuietMode"
    471                         + " NOT Broadcasting intent ACTION_SIM_STATE_CHANGED "
    472                         + " value=" +  value + " reason=" + reason);
    473                 return;
    474             }
    475 
    476             Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
    477             // TODO - we'd like this intent to have a single snapshot of all sim state,
    478             // but until then this should not use REPLACE_PENDING or we may lose
    479             // information
    480             // intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
    481             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    482             intent.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
    483             intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, value);
    484             intent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason);
    485             SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhoneId);
    486             log("broadcastIccStateChangedIntent intent ACTION_SIM_STATE_CHANGED value=" + value
    487                 + " reason=" + reason + " for mPhoneId=" + mPhoneId);
    488             ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE,
    489                     UserHandle.USER_ALL);
    490         }
    491     }
    492 
    493     private void broadcastInternalIccStateChangedIntent(String value, String reason) {
    494         synchronized (mLock) {
    495             if (mPhoneId == null) {
    496                 loge("broadcastInternalIccStateChangedIntent: Card Index is not set; Return!!");
    497                 return;
    498             }
    499 
    500             Intent intent = new Intent(ACTION_INTERNAL_SIM_STATE_CHANGED);
    501             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
    502                     | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    503             intent.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
    504             intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, value);
    505             intent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason);
    506             intent.putExtra(PhoneConstants.PHONE_KEY, mPhoneId);  // SubId may not be valid.
    507             log("Sending intent ACTION_INTERNAL_SIM_STATE_CHANGED" + " for mPhoneId : " + mPhoneId);
    508             ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
    509         }
    510     }
    511 
    512     private void setExternalState(State newState, boolean override) {
    513         synchronized (mLock) {
    514             if (mPhoneId == null || !SubscriptionManager.isValidSlotId(mPhoneId)) {
    515                 loge("setExternalState: mPhoneId=" + mPhoneId + " is invalid; Return!!");
    516                 return;
    517             }
    518 
    519             if (!override && newState == mExternalState) {
    520                 loge("setExternalState: !override and newstate unchanged from " + newState);
    521                 return;
    522             }
    523             mExternalState = newState;
    524             loge("setExternalState: set mPhoneId=" + mPhoneId + " mExternalState=" + mExternalState);
    525             mTelephonyManager.setSimStateForPhone(mPhoneId, getState().toString());
    526 
    527             // For locked states, we should be sending internal broadcast.
    528             if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(getIccStateIntentString(mExternalState))) {
    529                 broadcastInternalIccStateChangedIntent(getIccStateIntentString(mExternalState),
    530                         getIccStateReason(mExternalState));
    531             } else {
    532                 broadcastIccStateChangedIntent(getIccStateIntentString(mExternalState),
    533                         getIccStateReason(mExternalState));
    534             }
    535             // TODO: Need to notify registrants for other states as well.
    536             if ( State.ABSENT == mExternalState) {
    537                 mAbsentRegistrants.notifyRegistrants();
    538             }
    539         }
    540     }
    541 
    542     private void processLockedState() {
    543         synchronized (mLock) {
    544             if (mUiccApplication == null) {
    545                 //Don't need to do anything if non-existent application is locked
    546                 return;
    547             }
    548             PinState pin1State = mUiccApplication.getPin1State();
    549             if (pin1State == PinState.PINSTATE_ENABLED_PERM_BLOCKED) {
    550                 setExternalState(State.PERM_DISABLED);
    551                 return;
    552             }
    553 
    554             AppState appState = mUiccApplication.getState();
    555             switch (appState) {
    556                 case APPSTATE_PIN:
    557                     mPinLockedRegistrants.notifyRegistrants();
    558                     setExternalState(State.PIN_REQUIRED);
    559                     break;
    560                 case APPSTATE_PUK:
    561                     setExternalState(State.PUK_REQUIRED);
    562                     break;
    563                 case APPSTATE_DETECTED:
    564                 case APPSTATE_READY:
    565                 case APPSTATE_SUBSCRIPTION_PERSO:
    566                 case APPSTATE_UNKNOWN:
    567                     // Neither required
    568                     break;
    569             }
    570         }
    571     }
    572 
    573     private void setExternalState(State newState) {
    574         setExternalState(newState, false);
    575     }
    576 
    577     public boolean getIccRecordsLoaded() {
    578         synchronized (mLock) {
    579             if (mIccRecords != null) {
    580                 return mIccRecords.getRecordsLoaded();
    581             }
    582             return false;
    583         }
    584     }
    585 
    586     private String getIccStateIntentString(State state) {
    587         switch (state) {
    588             case ABSENT: return IccCardConstants.INTENT_VALUE_ICC_ABSENT;
    589             case PIN_REQUIRED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
    590             case PUK_REQUIRED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
    591             case NETWORK_LOCKED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
    592             case READY: return IccCardConstants.INTENT_VALUE_ICC_READY;
    593             case NOT_READY: return IccCardConstants.INTENT_VALUE_ICC_NOT_READY;
    594             case PERM_DISABLED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
    595             case CARD_IO_ERROR: return IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR;
    596             default: return IccCardConstants.INTENT_VALUE_ICC_UNKNOWN;
    597         }
    598     }
    599 
    600     /**
    601      * Locked state have a reason (PIN, PUK, NETWORK, PERM_DISABLED, CARD_IO_ERROR)
    602      * @return reason
    603      */
    604     private String getIccStateReason(State state) {
    605         switch (state) {
    606             case PIN_REQUIRED: return IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN;
    607             case PUK_REQUIRED: return IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK;
    608             case NETWORK_LOCKED: return IccCardConstants.INTENT_VALUE_LOCKED_NETWORK;
    609             case PERM_DISABLED: return IccCardConstants.INTENT_VALUE_ABSENT_ON_PERM_DISABLED;
    610             case CARD_IO_ERROR: return IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR;
    611             default: return null;
    612        }
    613     }
    614 
    615     /* IccCard interface implementation */
    616     @Override
    617     public State getState() {
    618         synchronized (mLock) {
    619             return mExternalState;
    620         }
    621     }
    622 
    623     @Override
    624     public IccRecords getIccRecords() {
    625         synchronized (mLock) {
    626             return mIccRecords;
    627         }
    628     }
    629 
    630     @Override
    631     public IccFileHandler getIccFileHandler() {
    632         synchronized (mLock) {
    633             if (mUiccApplication != null) {
    634                 return mUiccApplication.getIccFileHandler();
    635             }
    636             return null;
    637         }
    638     }
    639 
    640     /**
    641      * Notifies handler of any transition into State.ABSENT
    642      */
    643     @Override
    644     public void registerForAbsent(Handler h, int what, Object obj) {
    645         synchronized (mLock) {
    646             Registrant r = new Registrant (h, what, obj);
    647 
    648             mAbsentRegistrants.add(r);
    649 
    650             if (getState() == State.ABSENT) {
    651                 r.notifyRegistrant();
    652             }
    653         }
    654     }
    655 
    656     @Override
    657     public void unregisterForAbsent(Handler h) {
    658         synchronized (mLock) {
    659             mAbsentRegistrants.remove(h);
    660         }
    661     }
    662 
    663     /**
    664      * Notifies handler of any transition into State.NETWORK_LOCKED
    665      */
    666     @Override
    667     public void registerForNetworkLocked(Handler h, int what, Object obj) {
    668         synchronized (mLock) {
    669             Registrant r = new Registrant (h, what, obj);
    670 
    671             mNetworkLockedRegistrants.add(r);
    672 
    673             if (getState() == State.NETWORK_LOCKED) {
    674                 r.notifyRegistrant();
    675             }
    676         }
    677     }
    678 
    679     @Override
    680     public void unregisterForNetworkLocked(Handler h) {
    681         synchronized (mLock) {
    682             mNetworkLockedRegistrants.remove(h);
    683         }
    684     }
    685 
    686     /**
    687      * Notifies handler of any transition into State.isPinLocked()
    688      */
    689     @Override
    690     public void registerForLocked(Handler h, int what, Object obj) {
    691         synchronized (mLock) {
    692             Registrant r = new Registrant (h, what, obj);
    693 
    694             mPinLockedRegistrants.add(r);
    695 
    696             if (getState().isPinLocked()) {
    697                 r.notifyRegistrant();
    698             }
    699         }
    700     }
    701 
    702     @Override
    703     public void unregisterForLocked(Handler h) {
    704         synchronized (mLock) {
    705             mPinLockedRegistrants.remove(h);
    706         }
    707     }
    708 
    709     @Override
    710     public void supplyPin(String pin, Message onComplete) {
    711         synchronized (mLock) {
    712             if (mUiccApplication != null) {
    713                 mUiccApplication.supplyPin(pin, onComplete);
    714             } else if (onComplete != null) {
    715                 Exception e = new RuntimeException("ICC card is absent.");
    716                 AsyncResult.forMessage(onComplete).exception = e;
    717                 onComplete.sendToTarget();
    718                 return;
    719             }
    720         }
    721     }
    722 
    723     @Override
    724     public void supplyPuk(String puk, String newPin, Message onComplete) {
    725         synchronized (mLock) {
    726             if (mUiccApplication != null) {
    727                 mUiccApplication.supplyPuk(puk, newPin, onComplete);
    728             } else if (onComplete != null) {
    729                 Exception e = new RuntimeException("ICC card is absent.");
    730                 AsyncResult.forMessage(onComplete).exception = e;
    731                 onComplete.sendToTarget();
    732                 return;
    733             }
    734         }
    735     }
    736 
    737     @Override
    738     public void supplyPin2(String pin2, Message onComplete) {
    739         synchronized (mLock) {
    740             if (mUiccApplication != null) {
    741                 mUiccApplication.supplyPin2(pin2, onComplete);
    742             } else if (onComplete != null) {
    743                 Exception e = new RuntimeException("ICC card is absent.");
    744                 AsyncResult.forMessage(onComplete).exception = e;
    745                 onComplete.sendToTarget();
    746                 return;
    747             }
    748         }
    749     }
    750 
    751     @Override
    752     public void supplyPuk2(String puk2, String newPin2, Message onComplete) {
    753         synchronized (mLock) {
    754             if (mUiccApplication != null) {
    755                 mUiccApplication.supplyPuk2(puk2, newPin2, onComplete);
    756             } else if (onComplete != null) {
    757                 Exception e = new RuntimeException("ICC card is absent.");
    758                 AsyncResult.forMessage(onComplete).exception = e;
    759                 onComplete.sendToTarget();
    760                 return;
    761             }
    762         }
    763     }
    764 
    765     @Override
    766     public void supplyNetworkDepersonalization(String pin, Message onComplete) {
    767         synchronized (mLock) {
    768             if (mUiccApplication != null) {
    769                 mUiccApplication.supplyNetworkDepersonalization(pin, onComplete);
    770             } else if (onComplete != null) {
    771                 Exception e = new RuntimeException("CommandsInterface is not set.");
    772                 AsyncResult.forMessage(onComplete).exception = e;
    773                 onComplete.sendToTarget();
    774                 return;
    775             }
    776         }
    777     }
    778 
    779     @Override
    780     public boolean getIccLockEnabled() {
    781         synchronized (mLock) {
    782             /* defaults to false, if ICC is absent/deactivated */
    783             Boolean retValue = mUiccApplication != null ?
    784                     mUiccApplication.getIccLockEnabled() : false;
    785             return retValue;
    786         }
    787     }
    788 
    789     @Override
    790     public boolean getIccFdnEnabled() {
    791         synchronized (mLock) {
    792             Boolean retValue = mUiccApplication != null ?
    793                     mUiccApplication.getIccFdnEnabled() : false;
    794             return retValue;
    795         }
    796     }
    797 
    798     public boolean getIccFdnAvailable() {
    799         boolean retValue = mUiccApplication != null ? mUiccApplication.getIccFdnAvailable() : false;
    800         return retValue;
    801     }
    802 
    803     public boolean getIccPin2Blocked() {
    804         /* defaults to disabled */
    805         Boolean retValue = mUiccApplication != null ? mUiccApplication.getIccPin2Blocked() : false;
    806         return retValue;
    807     }
    808 
    809     public boolean getIccPuk2Blocked() {
    810         /* defaults to disabled */
    811         Boolean retValue = mUiccApplication != null ? mUiccApplication.getIccPuk2Blocked() : false;
    812         return retValue;
    813     }
    814 
    815     @Override
    816     public void setIccLockEnabled(boolean enabled, String password, Message onComplete) {
    817         synchronized (mLock) {
    818             if (mUiccApplication != null) {
    819                 mUiccApplication.setIccLockEnabled(enabled, password, onComplete);
    820             } else if (onComplete != null) {
    821                 Exception e = new RuntimeException("ICC card is absent.");
    822                 AsyncResult.forMessage(onComplete).exception = e;
    823                 onComplete.sendToTarget();
    824                 return;
    825             }
    826         }
    827     }
    828 
    829     @Override
    830     public void setIccFdnEnabled(boolean enabled, String password, Message onComplete) {
    831         synchronized (mLock) {
    832             if (mUiccApplication != null) {
    833                 mUiccApplication.setIccFdnEnabled(enabled, password, onComplete);
    834             } else if (onComplete != null) {
    835                 Exception e = new RuntimeException("ICC card is absent.");
    836                 AsyncResult.forMessage(onComplete).exception = e;
    837                 onComplete.sendToTarget();
    838                 return;
    839             }
    840         }
    841     }
    842 
    843     @Override
    844     public void changeIccLockPassword(String oldPassword, String newPassword, Message onComplete) {
    845         synchronized (mLock) {
    846             if (mUiccApplication != null) {
    847                 mUiccApplication.changeIccLockPassword(oldPassword, newPassword, onComplete);
    848             } else if (onComplete != null) {
    849                 Exception e = new RuntimeException("ICC card is absent.");
    850                 AsyncResult.forMessage(onComplete).exception = e;
    851                 onComplete.sendToTarget();
    852                 return;
    853             }
    854         }
    855     }
    856 
    857     @Override
    858     public void changeIccFdnPassword(String oldPassword, String newPassword, Message onComplete) {
    859         synchronized (mLock) {
    860             if (mUiccApplication != null) {
    861                 mUiccApplication.changeIccFdnPassword(oldPassword, newPassword, onComplete);
    862             } else if (onComplete != null) {
    863                 Exception e = new RuntimeException("ICC card is absent.");
    864                 AsyncResult.forMessage(onComplete).exception = e;
    865                 onComplete.sendToTarget();
    866                 return;
    867             }
    868         }
    869     }
    870 
    871     @Override
    872     public String getServiceProviderName() {
    873         synchronized (mLock) {
    874             if (mIccRecords != null) {
    875                 return mIccRecords.getServiceProviderName();
    876             }
    877             return null;
    878         }
    879     }
    880 
    881     @Override
    882     public boolean isApplicationOnIcc(IccCardApplicationStatus.AppType type) {
    883         synchronized (mLock) {
    884             Boolean retValue = mUiccCard != null ? mUiccCard.isApplicationOnIcc(type) : false;
    885             return retValue;
    886         }
    887     }
    888 
    889     @Override
    890     public boolean hasIccCard() {
    891         synchronized (mLock) {
    892             if (mUiccCard != null && mUiccCard.getCardState() != CardState.CARDSTATE_ABSENT) {
    893                 return true;
    894             }
    895             return false;
    896         }
    897     }
    898 
    899     private void setSystemProperty(String property, String value) {
    900         TelephonyManager.setTelephonyProperty(mPhoneId, property, value);
    901     }
    902 
    903     public IccRecords getIccRecord() {
    904         return mIccRecords;
    905     }
    906     private void log(String s) {
    907         Rlog.d(LOG_TAG, s);
    908     }
    909 
    910     private void loge(String msg) {
    911         Rlog.e(LOG_TAG, msg);
    912     }
    913 
    914     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    915         pw.println("IccCardProxy: " + this);
    916         pw.println(" mContext=" + mContext);
    917         pw.println(" mCi=" + mCi);
    918         pw.println(" mAbsentRegistrants: size=" + mAbsentRegistrants.size());
    919         for (int i = 0; i < mAbsentRegistrants.size(); i++) {
    920             pw.println("  mAbsentRegistrants[" + i + "]="
    921                     + ((Registrant)mAbsentRegistrants.get(i)).getHandler());
    922         }
    923         pw.println(" mPinLockedRegistrants: size=" + mPinLockedRegistrants.size());
    924         for (int i = 0; i < mPinLockedRegistrants.size(); i++) {
    925             pw.println("  mPinLockedRegistrants[" + i + "]="
    926                     + ((Registrant)mPinLockedRegistrants.get(i)).getHandler());
    927         }
    928         pw.println(" mNetworkLockedRegistrants: size=" + mNetworkLockedRegistrants.size());
    929         for (int i = 0; i < mNetworkLockedRegistrants.size(); i++) {
    930             pw.println("  mNetworkLockedRegistrants[" + i + "]="
    931                     + ((Registrant)mNetworkLockedRegistrants.get(i)).getHandler());
    932         }
    933         pw.println(" mCurrentAppType=" + mCurrentAppType);
    934         pw.println(" mUiccController=" + mUiccController);
    935         pw.println(" mUiccCard=" + mUiccCard);
    936         pw.println(" mUiccApplication=" + mUiccApplication);
    937         pw.println(" mIccRecords=" + mIccRecords);
    938         pw.println(" mCdmaSSM=" + mCdmaSSM);
    939         pw.println(" mRadioOn=" + mRadioOn);
    940         pw.println(" mQuietMode=" + mQuietMode);
    941         pw.println(" mInitialized=" + mInitialized);
    942         pw.println(" mExternalState=" + mExternalState);
    943 
    944         pw.flush();
    945     }
    946 }
    947