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.uicc;
18
19import android.content.Context;
20import android.os.AsyncResult;
21import android.os.Handler;
22import android.os.Message;
23import android.os.Registrant;
24import android.os.RegistrantList;
25import android.telephony.Rlog;
26import android.telephony.ServiceState;
27import android.telephony.SubscriptionInfo;
28import android.telephony.TelephonyManager;
29import android.text.TextUtils;
30
31import com.android.internal.telephony.CommandsInterface;
32
33import java.io.FileDescriptor;
34import java.io.PrintWriter;
35import java.io.UnsupportedEncodingException;
36import java.util.Arrays;
37import java.util.HashMap;
38import java.util.concurrent.atomic.AtomicBoolean;
39import java.util.concurrent.atomic.AtomicInteger;
40
41/**
42 * {@hide}
43 */
44public abstract class IccRecords extends Handler implements IccConstants {
45    protected static final boolean DBG = true;
46    protected static final boolean VDBG = false; // STOPSHIP if true
47
48    // ***** Instance Variables
49    protected AtomicBoolean mDestroyed = new AtomicBoolean(false);
50    protected AtomicBoolean mLoaded = new AtomicBoolean(false);
51    protected Context mContext;
52    protected CommandsInterface mCi;
53    protected IccFileHandler mFh;
54    protected UiccCardApplication mParentApp;
55    protected TelephonyManager mTelephonyManager;
56
57    protected RegistrantList mRecordsLoadedRegistrants = new RegistrantList();
58    protected RegistrantList mLockedRecordsLoadedRegistrants = new RegistrantList();
59    protected RegistrantList mNetworkLockedRecordsLoadedRegistrants = new RegistrantList();
60    protected RegistrantList mImsiReadyRegistrants = new RegistrantList();
61    protected RegistrantList mRecordsEventsRegistrants = new RegistrantList();
62    protected RegistrantList mNewSmsRegistrants = new RegistrantList();
63    protected RegistrantList mNetworkSelectionModeAutomaticRegistrants = new RegistrantList();
64    protected RegistrantList mSpnUpdatedRegistrants = new RegistrantList();
65    protected RegistrantList mRecordsOverrideRegistrants = new RegistrantList();
66
67    protected int mRecordsToLoad;  // number of pending load requests
68
69    protected AdnRecordCache mAdnCache;
70
71    // ***** Cached SIM State; cleared on channel close
72
73    // SIM is not locked
74    protected static final int LOCKED_RECORDS_REQ_REASON_NONE = 0;
75    // Records requested for PIN or PUK locked SIM
76    protected static final int LOCKED_RECORDS_REQ_REASON_LOCKED = 1;
77    // Records requested for network locked SIM
78    protected static final int LOCKED_RECORDS_REQ_REASON_NETWORK_LOCKED = 2;
79
80    protected boolean mRecordsRequested = false; // true if we've made requests for the sim records
81    protected int mLockedRecordsReqReason = LOCKED_RECORDS_REQ_REASON_NONE;
82
83    protected String mIccId;  // Includes only decimals (no hex)
84
85    protected String mFullIccId;  // Includes hex characters in ICCID
86    protected String mMsisdn = null;  // My mobile number
87    protected String mMsisdnTag = null;
88    protected String mNewMsisdn = null;
89    protected String mNewMsisdnTag = null;
90    protected String mVoiceMailNum = null;
91    protected String mVoiceMailTag = null;
92    protected String mNewVoiceMailNum = null;
93    protected String mNewVoiceMailTag = null;
94    protected boolean mIsVoiceMailFixed = false;
95    protected String mImsi;
96    private IccIoResult auth_rsp;
97
98    protected int mMncLength = UNINITIALIZED;
99    protected int mMailboxIndex = 0; // 0 is no mailbox dailing number associated
100
101    private String mSpn;
102
103    protected String mGid1;
104    protected String mGid2;
105
106    protected String mPnnHomeName;
107
108    protected String mPrefLang;
109
110    protected PlmnActRecord[] mHplmnActRecords;
111    protected PlmnActRecord[] mOplmnActRecords;
112    protected PlmnActRecord[] mPlmnActRecords;
113
114    protected String[] mEhplmns;
115    protected String[] mFplmns;
116
117    private final Object mLock = new Object();
118
119    CarrierTestOverride mCarrierTestOverride;
120
121    //Arbitrary offset for the Handler
122    protected static final int HANDLER_ACTION_BASE = 0x12E500;
123    protected static final int HANDLER_ACTION_NONE = HANDLER_ACTION_BASE + 0;
124    protected static final int HANDLER_ACTION_SEND_RESPONSE = HANDLER_ACTION_BASE + 1;
125    protected static AtomicInteger sNextRequestId = new AtomicInteger(1);
126    protected final HashMap<Integer, Message> mPendingResponses = new HashMap<>();
127
128    // ***** Constants
129
130    // Markers for mncLength
131    protected static final int UNINITIALIZED = -1;
132    protected static final int UNKNOWN = 0;
133
134    // Bitmasks for SPN display rules.
135    public static final int SPN_RULE_SHOW_SPN  = 0x01;
136    public static final int SPN_RULE_SHOW_PLMN = 0x02;
137
138    // ***** Event Constants
139    public static final int EVENT_MWI = 0; // Message Waiting indication
140    public static final int EVENT_CFI = 1; // Call Forwarding indication
141    public static final int EVENT_SPN = 2; // Service Provider Name
142
143    public static final int EVENT_GET_ICC_RECORD_DONE = 100;
144    public static final int EVENT_REFRESH = 31; // ICC refresh occurred
145    protected static final int EVENT_APP_READY = 1;
146    private static final int EVENT_AKA_AUTHENTICATE_DONE          = 90;
147
148    public static final int CALL_FORWARDING_STATUS_DISABLED = 0;
149    public static final int CALL_FORWARDING_STATUS_ENABLED = 1;
150    public static final int CALL_FORWARDING_STATUS_UNKNOWN = -1;
151
152    public static final int DEFAULT_VOICE_MESSAGE_COUNT = -2;
153    public static final int UNKNOWN_VOICE_MESSAGE_COUNT = -1;
154
155    @Override
156    public String toString() {
157        String iccIdToPrint = SubscriptionInfo.givePrintableIccid(mFullIccId);
158        return "mDestroyed=" + mDestroyed
159                + " mContext=" + mContext
160                + " mCi=" + mCi
161                + " mFh=" + mFh
162                + " mParentApp=" + mParentApp
163                + " recordsToLoad=" + mRecordsToLoad
164                + " adnCache=" + mAdnCache
165                + " recordsRequested=" + mRecordsRequested
166                + " lockedRecordsReqReason=" + mLockedRecordsReqReason
167                + " iccid=" + iccIdToPrint
168                + (mCarrierTestOverride.isInTestMode() ? "mFakeIccid="
169                + mCarrierTestOverride.getFakeIccid() : "")
170                + " msisdnTag=" + mMsisdnTag
171                + " voiceMailNum=" + Rlog.pii(VDBG, mVoiceMailNum)
172                + " voiceMailTag=" + mVoiceMailTag
173                + " voiceMailNum=" + Rlog.pii(VDBG, mNewVoiceMailNum)
174                + " newVoiceMailTag=" + mNewVoiceMailTag
175                + " isVoiceMailFixed=" + mIsVoiceMailFixed
176                + " mImsi=" + ((mImsi != null) ?
177                mImsi.substring(0, 6) + Rlog.pii(VDBG, mImsi.substring(6)) : "null")
178                + (mCarrierTestOverride.isInTestMode() ? " mFakeImsi="
179                + mCarrierTestOverride.getFakeIMSI() : "")
180                + " mncLength=" + mMncLength
181                + " mailboxIndex=" + mMailboxIndex
182                + " spn=" + mSpn
183                + (mCarrierTestOverride.isInTestMode() ? " mFakeSpn="
184                + mCarrierTestOverride.getFakeSpn() : "");
185    }
186
187    /**
188     * Generic ICC record loaded callback. Subclasses can call EF load methods on
189     * {@link IccFileHandler} passing a Message for onLoaded with the what field set to
190     * {@link #EVENT_GET_ICC_RECORD_DONE} and the obj field set to an instance
191     * of this interface. The {@link #handleMessage} method in this class will print a
192     * log message using {@link #getEfName()} and decrement {@link #mRecordsToLoad}.
193     *
194     * If the record load was successful, {@link #onRecordLoaded} will be called with the result.
195     * Otherwise, an error log message will be output by {@link #handleMessage} and
196     * {@link #onRecordLoaded} will not be called.
197     */
198    public interface IccRecordLoaded {
199        String getEfName();
200        void onRecordLoaded(AsyncResult ar);
201    }
202
203    // ***** Constructor
204    public IccRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
205        mContext = c;
206        mCi = ci;
207        mFh = app.getIccFileHandler();
208        mParentApp = app;
209        mTelephonyManager = (TelephonyManager) mContext.getSystemService(
210                Context.TELEPHONY_SERVICE);
211
212        mCarrierTestOverride = new CarrierTestOverride();
213        mCi.registerForIccRefresh(this, EVENT_REFRESH, null);
214    }
215
216    // Override IccRecords for testing
217    public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1,
218            String gid2, String pnn, String spn)  {
219        mCarrierTestOverride.override(mccmnc, imsi, iccid, gid1, gid2, pnn, spn);
220        mTelephonyManager.setSimOperatorNameForPhone(mParentApp.getPhoneId(), spn);
221        mTelephonyManager.setSimOperatorNumericForPhone(mParentApp.getPhoneId(), mccmnc);
222        mRecordsOverrideRegistrants.notifyRegistrants();
223    }
224
225    /**
226     * Call when the IccRecords object is no longer going to be used.
227     */
228    public void dispose() {
229        mDestroyed.set(true);
230
231        // It is possible that there is another thread waiting for the response
232        // to requestIccSimAuthentication() in getIccSimChallengeResponse().
233        auth_rsp = null;
234        synchronized (mLock) {
235            mLock.notifyAll();
236        }
237
238        mCi.unregisterForIccRefresh(this);
239        mParentApp = null;
240        mFh = null;
241        mCi = null;
242        mContext = null;
243        if (mAdnCache != null) {
244            mAdnCache.reset();
245        }
246        mLoaded.set(false);
247    }
248
249    public abstract void onReady();
250
251    //***** Public Methods
252    public AdnRecordCache getAdnCache() {
253        return mAdnCache;
254    }
255
256    /**
257     * Adds a message to the pending requests list by generating a unique
258     * (integer) hash key and returning it. The message should never be null.
259     */
260    public int storePendingResponseMessage(Message msg) {
261        int key = sNextRequestId.getAndIncrement();
262        synchronized (mPendingResponses) {
263            mPendingResponses.put(key, msg);
264        }
265        return key;
266    }
267
268    /**
269     * Returns the pending request, if any or null
270     */
271    public Message retrievePendingResponseMessage(Integer key) {
272        Message m;
273        synchronized (mPendingResponses) {
274            return mPendingResponses.remove(key);
275        }
276    }
277
278    /**
279     * Returns the ICC ID stripped at the first hex character. Some SIMs have ICC IDs
280     * containing hex digits; {@link #getFullIccId()} should be used to get the full ID including
281     * hex digits.
282     * @return ICC ID without hex digits
283     */
284    public String getIccId() {
285        if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeIccid() != null) {
286            return mCarrierTestOverride.getFakeIccid();
287        } else {
288            return mIccId;
289        }
290    }
291
292    /**
293     * Returns the full ICC ID including hex digits.
294     * @return full ICC ID including hex digits
295     */
296    public String getFullIccId() {
297        return mFullIccId;
298    }
299
300    public void registerForRecordsLoaded(Handler h, int what, Object obj) {
301        if (mDestroyed.get()) {
302            return;
303        }
304
305        Registrant r = new Registrant(h, what, obj);
306        mRecordsLoadedRegistrants.add(r);
307
308        if (getRecordsLoaded()) {
309            r.notifyRegistrant(new AsyncResult(null, null, null));
310        }
311    }
312
313    public void unregisterForRecordsLoaded(Handler h) {
314        mRecordsLoadedRegistrants.remove(h);
315    }
316
317    public void unregisterForRecordsOverride(Handler h) {
318        mRecordsOverrideRegistrants.remove(h);
319    }
320
321    public void registerForRecordsOverride(Handler h, int what, Object obj) {
322        if (mDestroyed.get()) {
323            return;
324        }
325
326        Registrant r = new Registrant(h, what, obj);
327        mRecordsOverrideRegistrants.add(r);
328
329        if (getRecordsLoaded()) {
330            r.notifyRegistrant(new AsyncResult(null, null, null));
331        }
332    }
333
334    /**
335     * Register to be notified when records are loaded for a PIN or PUK locked SIM
336     */
337    public void registerForLockedRecordsLoaded(Handler h, int what, Object obj) {
338        if (mDestroyed.get()) {
339            return;
340        }
341
342        Registrant r = new Registrant(h, what, obj);
343        mLockedRecordsLoadedRegistrants.add(r);
344
345        if (getLockedRecordsLoaded()) {
346            r.notifyRegistrant(new AsyncResult(null, null, null));
347        }
348    }
349
350    /**
351     * Unregister corresponding to registerForLockedRecordsLoaded()
352     */
353    public void unregisterForLockedRecordsLoaded(Handler h) {
354        mLockedRecordsLoadedRegistrants.remove(h);
355    }
356
357    /**
358     * Register to be notified when records are loaded for a network locked SIM
359     */
360    public void registerForNetworkLockedRecordsLoaded(Handler h, int what, Object obj) {
361        if (mDestroyed.get()) {
362            return;
363        }
364
365        Registrant r = new Registrant(h, what, obj);
366        mNetworkLockedRecordsLoadedRegistrants.add(r);
367
368        if (getNetworkLockedRecordsLoaded()) {
369            r.notifyRegistrant(new AsyncResult(null, null, null));
370        }
371    }
372
373    /**
374     * Unregister corresponding to registerForLockedRecordsLoaded()
375     */
376    public void unregisterForNetworkLockedRecordsLoaded(Handler h) {
377        mNetworkLockedRecordsLoadedRegistrants.remove(h);
378    }
379
380    public void registerForImsiReady(Handler h, int what, Object obj) {
381        if (mDestroyed.get()) {
382            return;
383        }
384
385        Registrant r = new Registrant(h, what, obj);
386        mImsiReadyRegistrants.add(r);
387
388        if (getIMSI() != null) {
389            r.notifyRegistrant(new AsyncResult(null, null, null));
390        }
391    }
392    public void unregisterForImsiReady(Handler h) {
393        mImsiReadyRegistrants.remove(h);
394    }
395
396    public void registerForSpnUpdate(Handler h, int what, Object obj) {
397        if (mDestroyed.get()) {
398            return;
399        }
400
401        Registrant r = new Registrant(h, what, obj);
402        mSpnUpdatedRegistrants.add(r);
403
404        if (!TextUtils.isEmpty(mSpn)) {
405            r.notifyRegistrant(new AsyncResult(null, null, null));
406        }
407    }
408    public void unregisterForSpnUpdate(Handler h) {
409        mSpnUpdatedRegistrants.remove(h);
410    }
411
412    public void registerForRecordsEvents(Handler h, int what, Object obj) {
413        Registrant r = new Registrant (h, what, obj);
414        mRecordsEventsRegistrants.add(r);
415
416        /* Notify registrant of all the possible events. This is to make sure registrant is
417        notified even if event occurred in the past. */
418        r.notifyResult(EVENT_MWI);
419        r.notifyResult(EVENT_CFI);
420    }
421    public void unregisterForRecordsEvents(Handler h) {
422        mRecordsEventsRegistrants.remove(h);
423    }
424
425    public void registerForNewSms(Handler h, int what, Object obj) {
426        Registrant r = new Registrant (h, what, obj);
427        mNewSmsRegistrants.add(r);
428    }
429    public void unregisterForNewSms(Handler h) {
430        mNewSmsRegistrants.remove(h);
431    }
432
433    public void registerForNetworkSelectionModeAutomatic(
434            Handler h, int what, Object obj) {
435        Registrant r = new Registrant (h, what, obj);
436        mNetworkSelectionModeAutomaticRegistrants.add(r);
437    }
438    public void unregisterForNetworkSelectionModeAutomatic(Handler h) {
439        mNetworkSelectionModeAutomaticRegistrants.remove(h);
440    }
441
442    /**
443     * Get the International Mobile Subscriber ID (IMSI) on a SIM
444     * for GSM, UMTS and like networks. Default is null if IMSI is
445     * not supported or unavailable.
446     *
447     * @return null if SIM is not yet ready or unavailable
448     */
449    public String getIMSI() {
450        if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeIMSI() != null) {
451            return mCarrierTestOverride.getFakeIMSI();
452        } else {
453            return mImsi;
454        }
455    }
456
457    /**
458     * Imsi could be set by ServiceStateTrackers in case of cdma
459     * @param imsi
460     */
461    public void setImsi(String imsi) {
462        mImsi = imsi;
463        mImsiReadyRegistrants.notifyRegistrants();
464    }
465
466    /**
467     * Get the Network Access ID (NAI) on a CSIM for CDMA like networks. Default is null if IMSI is
468     * not supported or unavailable.
469     *
470     * @return null if NAI is not yet ready or unavailable
471     */
472    public String getNAI() {
473        return null;
474    }
475
476    public String getMsisdnNumber() {
477        return mMsisdn;
478    }
479
480    /**
481     * Get the Group Identifier Level 1 (GID1) on a SIM for GSM.
482     * @return null if SIM is not yet ready
483     */
484    public String getGid1() {
485        if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeGid1() != null) {
486            return mCarrierTestOverride.getFakeGid1();
487        } else {
488            return mGid1;
489        }
490    }
491
492    /**
493     * Get the Group Identifier Level 2 (GID2) on a SIM.
494     * @return null if SIM is not yet ready
495     */
496    public String getGid2() {
497        if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeGid2() != null) {
498            return mCarrierTestOverride.getFakeGid2();
499        } else {
500            return mGid2;
501        }
502    }
503
504    /**
505     * Get the PLMN network name on a SIM.
506     * @return null if SIM is not yet ready
507     */
508    public String getPnnHomeName() {
509        if (mCarrierTestOverride.isInTestMode()
510                && mCarrierTestOverride.getFakePnnHomeName() != null) {
511            return mCarrierTestOverride.getFakePnnHomeName();
512        } else {
513            return mPnnHomeName;
514        }
515    }
516
517    public void setMsisdnNumber(String alphaTag, String number,
518            Message onComplete) {
519        loge("setMsisdn() should not be invoked on base IccRecords");
520        // synthesize a "File Not Found" exception and return it
521        AsyncResult.forMessage(onComplete).exception =
522            (new IccIoResult(0x6A, 0x82, (byte[]) null)).getException();
523        onComplete.sendToTarget();
524    }
525
526    public String getMsisdnAlphaTag() {
527        return mMsisdnTag;
528    }
529
530    public String getVoiceMailNumber() {
531        return mVoiceMailNum;
532    }
533
534    /**
535     * Return Service Provider Name stored in SIM (EF_SPN=0x6F46) or in RUIM (EF_RUIM_SPN=0x6F41).
536     *
537     * @return null if SIM is not yet ready or no RUIM entry
538     */
539    public String getServiceProviderName() {
540        if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeSpn() != null) {
541            return mCarrierTestOverride.getFakeSpn();
542        }
543        String providerName = mSpn;
544
545        // Check for null pointers, mParentApp can be null after dispose,
546        // which did occur after removing a SIM.
547        UiccCardApplication parentApp = mParentApp;
548        if (parentApp != null) {
549            UiccProfile profile = parentApp.getUiccProfile();
550            if (profile != null) {
551                String brandOverride = profile.getOperatorBrandOverride();
552                if (brandOverride != null) {
553                    log("getServiceProviderName: override, providerName=" + providerName);
554                    providerName = brandOverride;
555                } else {
556                    log("getServiceProviderName: no brandOverride, providerName=" + providerName);
557                }
558            } else {
559                log("getServiceProviderName: card is null, providerName=" + providerName);
560            }
561        } else {
562            log("getServiceProviderName: mParentApp is null, providerName=" + providerName);
563        }
564        return providerName;
565    }
566
567    protected void setServiceProviderName(String spn) {
568        if (!TextUtils.equals(mSpn, spn)) {
569            mSpnUpdatedRegistrants.notifyRegistrants();
570            mSpn = spn;
571        }
572    }
573
574    /**
575     * Set voice mail number to SIM record
576     *
577     * The voice mail number can be stored either in EF_MBDN (TS 51.011) or
578     * EF_MAILBOX_CPHS (CPHS 4.2)
579     *
580     * If EF_MBDN is available, store the voice mail number to EF_MBDN
581     *
582     * If EF_MAILBOX_CPHS is enabled, store the voice mail number to EF_CHPS
583     *
584     * So the voice mail number will be stored in both EFs if both are available
585     *
586     * Return error only if both EF_MBDN and EF_MAILBOX_CPHS fail.
587     *
588     * When the operation is complete, onComplete will be sent to its handler
589     *
590     * @param alphaTag alpha-tagging of the dailing nubmer (upto 10 characters)
591     * @param voiceNumber dailing nubmer (upto 20 digits)
592     *        if the number is start with '+', then set to international TOA
593     * @param onComplete
594     *        onComplete.obj will be an AsyncResult
595     *        ((AsyncResult)onComplete.obj).exception == null on success
596     *        ((AsyncResult)onComplete.obj).exception != null on fail
597     */
598    public abstract void setVoiceMailNumber(String alphaTag, String voiceNumber,
599            Message onComplete);
600
601    public String getVoiceMailAlphaTag() {
602        return mVoiceMailTag;
603    }
604
605    /**
606     * Sets the SIM voice message waiting indicator records
607     * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported
608     * @param countWaiting The number of messages waiting, if known. Use
609     *                     -1 to indicate that an unknown number of
610     *                      messages are waiting
611     */
612    public abstract void setVoiceMessageWaiting(int line, int countWaiting);
613
614    /**
615     * Called by GsmCdmaPhone to update VoiceMail count
616     */
617    public abstract int getVoiceMessageCount();
618
619    /**
620     * Called by STK Service when REFRESH is received.
621     * @param fileChanged indicates whether any files changed
622     * @param fileList if non-null, a list of EF files that changed
623     */
624    public abstract void onRefresh(boolean fileChanged, int[] fileList);
625
626    public boolean getRecordsLoaded() {
627        return mRecordsToLoad == 0 && mRecordsRequested;
628    }
629
630    protected boolean getLockedRecordsLoaded() {
631        return mRecordsToLoad == 0
632                && mLockedRecordsReqReason == LOCKED_RECORDS_REQ_REASON_LOCKED;
633    }
634
635    protected boolean getNetworkLockedRecordsLoaded() {
636        return mRecordsToLoad == 0
637                && mLockedRecordsReqReason == LOCKED_RECORDS_REQ_REASON_NETWORK_LOCKED;
638    }
639
640    //***** Overridden from Handler
641    @Override
642    public void handleMessage(Message msg) {
643        AsyncResult ar;
644
645        switch (msg.what) {
646            case EVENT_GET_ICC_RECORD_DONE:
647                try {
648                    ar = (AsyncResult) msg.obj;
649                    IccRecordLoaded recordLoaded = (IccRecordLoaded) ar.userObj;
650                    if (DBG) log(recordLoaded.getEfName() + " LOADED");
651
652                    if (ar.exception != null) {
653                        loge("Record Load Exception: " + ar.exception);
654                    } else {
655                        recordLoaded.onRecordLoaded(ar);
656                    }
657                }catch (RuntimeException exc) {
658                    // I don't want these exceptions to be fatal
659                    loge("Exception parsing SIM record: " + exc);
660                } finally {
661                    // Count up record load responses even if they are fails
662                    onRecordLoaded();
663                }
664                break;
665
666            case EVENT_REFRESH:
667                ar = (AsyncResult)msg.obj;
668                if (DBG) log("Card REFRESH occurred: ");
669                if (ar.exception == null) {
670                    handleRefresh((IccRefreshResponse)ar.result);
671                } else {
672                    loge("Icc refresh Exception: " + ar.exception);
673                }
674                break;
675
676            case EVENT_AKA_AUTHENTICATE_DONE:
677                ar = (AsyncResult)msg.obj;
678                auth_rsp = null;
679                if (DBG) log("EVENT_AKA_AUTHENTICATE_DONE");
680                if (ar.exception != null) {
681                    loge("Exception ICC SIM AKA: " + ar.exception);
682                } else {
683                    try {
684                        auth_rsp = (IccIoResult)ar.result;
685                        if (DBG) log("ICC SIM AKA: auth_rsp = " + auth_rsp);
686                    } catch (Exception e) {
687                        loge("Failed to parse ICC SIM AKA contents: " + e);
688                    }
689                }
690                synchronized (mLock) {
691                    mLock.notifyAll();
692                }
693
694                break;
695
696            default:
697                super.handleMessage(msg);
698        }
699    }
700
701    /**
702     * Returns the SIM language derived from the EF-LI and EF-PL sim records.
703     */
704    public String getSimLanguage() {
705        return mPrefLang;
706    }
707
708    protected void setSimLanguage(byte[] efLi, byte[] efPl) {
709        String[] locales = mContext.getAssets().getLocales();
710        try {
711            mPrefLang = findBestLanguage(efLi, locales);
712        } catch (UnsupportedEncodingException uee) {
713            log("Unable to parse EF-LI: " + Arrays.toString(efLi));
714        }
715
716        if (mPrefLang == null) {
717            try {
718                mPrefLang = findBestLanguage(efPl, locales);
719            } catch (UnsupportedEncodingException uee) {
720                log("Unable to parse EF-PL: " + Arrays.toString(efLi));
721            }
722        }
723    }
724
725    protected static String findBestLanguage(byte[] languages, String[] locales)
726            throws UnsupportedEncodingException {
727        if ((languages == null) || (locales == null)) return null;
728
729        // Each 2-bytes consists of one language
730        for (int i = 0; (i + 1) < languages.length; i += 2) {
731            String lang = new String(languages, i, 2, "ISO-8859-1");
732            for (int j = 0; j < locales.length; j++) {
733                if (locales[j] != null && locales[j].length() >= 2 &&
734                        locales[j].substring(0, 2).equalsIgnoreCase(lang)) {
735                    return lang;
736                }
737            }
738        }
739
740        // no match found. return null
741        return null;
742    }
743
744    protected abstract void handleFileUpdate(int efid);
745
746    protected void handleRefresh(IccRefreshResponse refreshResponse){
747        if (refreshResponse == null) {
748            if (DBG) log("handleRefresh received without input");
749            return;
750        }
751
752        if (!TextUtils.isEmpty(refreshResponse.aid) &&
753                !refreshResponse.aid.equals(mParentApp.getAid())) {
754            // This is for different app. Ignore.
755            return;
756        }
757
758        switch (refreshResponse.refreshResult) {
759            case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE:
760                if (DBG) log("handleRefresh with SIM_FILE_UPDATED");
761                handleFileUpdate(refreshResponse.efId);
762                break;
763            default:
764                // unknown refresh operation
765                if (DBG) log("handleRefresh with unknown operation");
766                break;
767        }
768    }
769
770    protected abstract void onRecordLoaded();
771
772    protected abstract void onAllRecordsLoaded();
773
774    /**
775     * Returns the SpnDisplayRule based on settings on the SIM and the
776     * current service state. See TS 22.101 Annex A and TS 51.011 10.3.11
777     * for details.
778     *
779     * If the SPN is not found on the SIM, the rule is always PLMN_ONLY.
780     * Generally used for GSM/UMTS and the like SIMs.
781     *
782     * @param serviceState Service state
783     * @return the display rule
784     *
785     * @see #SPN_RULE_SHOW_SPN
786     * @see #SPN_RULE_SHOW_PLMN
787     */
788    public abstract int getDisplayRule(ServiceState serviceState);
789
790    /**
791     * Return true if "Restriction of menu options for manual PLMN selection"
792     * bit is set or EF_CSP data is unavailable, return false otherwise.
793     * Generally used for GSM/UMTS and the like SIMs.
794     */
795    public boolean isCspPlmnEnabled() {
796        return false;
797    }
798
799    /**
800     * Returns the 5 or 6 digit MCC/MNC of the operator that
801     * provided the SIM card. Returns null of SIM is not yet ready
802     * or is not valid for the type of IccCard. Generally used for
803     * GSM/UMTS and the like SIMS
804     */
805    public String getOperatorNumeric() {
806        return null;
807    }
808
809    /**
810     * Get the current Voice call forwarding flag for GSM/UMTS and the like SIMs
811     *
812     * @return CALL_FORWARDING_STATUS_XXX (DISABLED/ENABLED/UNKNOWN)
813     */
814    public int getVoiceCallForwardingFlag() {
815        return CALL_FORWARDING_STATUS_UNKNOWN;
816    }
817
818    /**
819     * Set the voice call forwarding flag for GSM/UMTS and the like SIMs
820     *
821     * @param line to enable/disable
822     * @param enable
823     * @param number to which CFU is enabled
824     */
825    public void setVoiceCallForwardingFlag(int line, boolean enable, String number) {
826    }
827
828    /**
829     * Indicates wether the ICC records have been loaded or not
830     *
831     * @return true if the records have been loaded, false otherwise.
832     */
833    public boolean isLoaded() {
834        return mLoaded.get();
835    }
836
837    /**
838     * Indicates wether SIM is in provisioned state or not.
839     * Overridden only if SIM can be dynamically provisioned via OTA.
840     *
841     * @return true if provisioned
842     */
843    public boolean isProvisioned () {
844        return true;
845    }
846
847    /**
848     * Write string to log file
849     *
850     * @param s is the string to write
851     */
852    protected abstract void log(String s);
853
854    /**
855     * Write error string to log file.
856     *
857     * @param s is the string to write
858     */
859    protected abstract void loge(String s);
860
861    /**
862     * Return an interface to retrieve the ISIM records for IMS, if available.
863     * @return the interface to retrieve the ISIM records, or null if not supported
864     */
865    public IsimRecords getIsimRecords() {
866        return null;
867    }
868
869    public UsimServiceTable getUsimServiceTable() {
870        return null;
871    }
872
873    protected void setSystemProperty(String key, String val) {
874        TelephonyManager.getDefault().setTelephonyProperty(mParentApp.getPhoneId(), key, val);
875
876        log("[key, value]=" + key + ", " +  val);
877    }
878
879    /**
880     * Returns the response of the SIM application on the UICC to authentication
881     * challenge/response algorithm. The data string and challenge response are
882     * Base64 encoded Strings.
883     * Can support EAP-SIM, EAP-AKA with results encoded per 3GPP TS 31.102.
884     *
885     * @param authContext parameter P2 that specifies the authentication context per 3GPP TS 31.102 (Section 7.1.2)
886     * @param data authentication challenge data
887     * @return challenge response
888     */
889    public String getIccSimChallengeResponse(int authContext, String data) {
890        if (DBG) log("getIccSimChallengeResponse:");
891
892        try {
893            synchronized(mLock) {
894                CommandsInterface ci = mCi;
895                UiccCardApplication parentApp = mParentApp;
896                if (ci != null && parentApp != null) {
897                    ci.requestIccSimAuthentication(authContext, data,
898                            parentApp.getAid(),
899                            obtainMessage(EVENT_AKA_AUTHENTICATE_DONE));
900                    try {
901                        mLock.wait();
902                    } catch (InterruptedException e) {
903                        loge("getIccSimChallengeResponse: Fail, interrupted"
904                                + " while trying to request Icc Sim Auth");
905                        return null;
906                    }
907                } else {
908                    loge( "getIccSimChallengeResponse: "
909                            + "Fail, ci or parentApp is null");
910                    return null;
911                }
912            }
913        } catch(Exception e) {
914            loge( "getIccSimChallengeResponse: "
915                    + "Fail while trying to request Icc Sim Auth");
916            return null;
917        }
918
919        if (auth_rsp == null) {
920            loge("getIccSimChallengeResponse: No authentication response");
921            return null;
922        }
923
924        if (DBG) log("getIccSimChallengeResponse: return auth_rsp");
925
926        return android.util.Base64.encodeToString(auth_rsp.payload, android.util.Base64.NO_WRAP);
927    }
928
929    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
930        pw.println("IccRecords: " + this);
931        pw.println(" mDestroyed=" + mDestroyed);
932        pw.println(" mCi=" + mCi);
933        pw.println(" mFh=" + mFh);
934        pw.println(" mParentApp=" + mParentApp);
935        pw.println(" recordsLoadedRegistrants: size=" + mRecordsLoadedRegistrants.size());
936        for (int i = 0; i < mRecordsLoadedRegistrants.size(); i++) {
937            pw.println("  recordsLoadedRegistrants[" + i + "]="
938                    + ((Registrant)mRecordsLoadedRegistrants.get(i)).getHandler());
939        }
940        pw.println(" mLockedRecordsLoadedRegistrants: size="
941                + mLockedRecordsLoadedRegistrants.size());
942        for (int i = 0; i < mLockedRecordsLoadedRegistrants.size(); i++) {
943            pw.println("  mLockedRecordsLoadedRegistrants[" + i + "]="
944                    + ((Registrant) mLockedRecordsLoadedRegistrants.get(i)).getHandler());
945        }
946        pw.println(" mNetworkLockedRecordsLoadedRegistrants: size="
947                + mNetworkLockedRecordsLoadedRegistrants.size());
948        for (int i = 0; i < mNetworkLockedRecordsLoadedRegistrants.size(); i++) {
949            pw.println("  mLockedRecordsLoadedRegistrants[" + i + "]="
950                    + ((Registrant) mNetworkLockedRecordsLoadedRegistrants.get(i)).getHandler());
951        }
952        pw.println(" mImsiReadyRegistrants: size=" + mImsiReadyRegistrants.size());
953        for (int i = 0; i < mImsiReadyRegistrants.size(); i++) {
954            pw.println("  mImsiReadyRegistrants[" + i + "]="
955                    + ((Registrant)mImsiReadyRegistrants.get(i)).getHandler());
956        }
957        pw.println(" mRecordsEventsRegistrants: size=" + mRecordsEventsRegistrants.size());
958        for (int i = 0; i < mRecordsEventsRegistrants.size(); i++) {
959            pw.println("  mRecordsEventsRegistrants[" + i + "]="
960                    + ((Registrant)mRecordsEventsRegistrants.get(i)).getHandler());
961        }
962        pw.println(" mNewSmsRegistrants: size=" + mNewSmsRegistrants.size());
963        for (int i = 0; i < mNewSmsRegistrants.size(); i++) {
964            pw.println("  mNewSmsRegistrants[" + i + "]="
965                    + ((Registrant)mNewSmsRegistrants.get(i)).getHandler());
966        }
967        pw.println(" mNetworkSelectionModeAutomaticRegistrants: size="
968                + mNetworkSelectionModeAutomaticRegistrants.size());
969        for (int i = 0; i < mNetworkSelectionModeAutomaticRegistrants.size(); i++) {
970            pw.println("  mNetworkSelectionModeAutomaticRegistrants[" + i + "]="
971                    + ((Registrant)mNetworkSelectionModeAutomaticRegistrants.get(i)).getHandler());
972        }
973        pw.println(" mRecordsRequested=" + mRecordsRequested);
974        pw.println(" mLockedRecordsReqReason=" + mLockedRecordsReqReason);
975        pw.println(" mRecordsToLoad=" + mRecordsToLoad);
976        pw.println(" mRdnCache=" + mAdnCache);
977
978        String iccIdToPrint = SubscriptionInfo.givePrintableIccid(mFullIccId);
979        pw.println(" iccid=" + iccIdToPrint);
980        pw.println(" mMsisdn=" + Rlog.pii(VDBG, mMsisdn));
981        pw.println(" mMsisdnTag=" + mMsisdnTag);
982        pw.println(" mVoiceMailNum=" + Rlog.pii(VDBG, mVoiceMailNum));
983        pw.println(" mVoiceMailTag=" + mVoiceMailTag);
984        pw.println(" mNewVoiceMailNum=" + Rlog.pii(VDBG, mNewVoiceMailNum));
985        pw.println(" mNewVoiceMailTag=" + mNewVoiceMailTag);
986        pw.println(" mIsVoiceMailFixed=" + mIsVoiceMailFixed);
987        pw.println(" mImsi=" + ((mImsi != null) ?
988                mImsi.substring(0, 6) + Rlog.pii(VDBG, mImsi.substring(6)) : "null"));
989        if (mCarrierTestOverride.isInTestMode()) {
990            pw.println(" mFakeImsi=" + mCarrierTestOverride.getFakeIMSI());
991        }
992        pw.println(" mMncLength=" + mMncLength);
993        pw.println(" mMailboxIndex=" + mMailboxIndex);
994        pw.println(" mSpn=" + mSpn);
995        if (mCarrierTestOverride.isInTestMode()) {
996            pw.println(" mFakeSpn=" + mCarrierTestOverride.getFakeSpn());
997        }
998        pw.flush();
999    }
1000}
1001