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
17package com.android.internal.telephony;
18
19import static android.Manifest.permission.READ_PHONE_STATE;
20import android.app.ActivityManagerNative;
21import android.app.AlertDialog;
22import android.content.Context;
23import android.content.DialogInterface;
24import android.content.Intent;
25import android.content.res.Resources;
26import android.os.AsyncResult;
27import android.os.Handler;
28import android.os.Message;
29import android.os.PowerManager;
30import android.os.Registrant;
31import android.os.RegistrantList;
32import android.util.Log;
33import android.view.WindowManager;
34
35import com.android.internal.telephony.PhoneBase;
36import com.android.internal.telephony.CommandsInterface.RadioState;
37import com.android.internal.telephony.gsm.SIMFileHandler;
38import com.android.internal.telephony.gsm.SIMRecords;
39import com.android.internal.telephony.cat.CatService;
40import com.android.internal.telephony.cdma.CDMALTEPhone;
41import com.android.internal.telephony.cdma.CdmaLteUiccFileHandler;
42import com.android.internal.telephony.cdma.CdmaLteUiccRecords;
43import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
44import com.android.internal.telephony.cdma.RuimFileHandler;
45import com.android.internal.telephony.cdma.RuimRecords;
46
47import android.os.SystemProperties;
48
49import com.android.internal.R;
50
51/**
52 * {@hide}
53 */
54public 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