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