SIMRecords.java revision 67aacea4e6b0199579a71b3a6a2f5d6778b8a002
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 static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
20import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
21import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
22import android.content.Context;
23import android.os.AsyncResult;
24import android.os.Message;
25import android.os.SystemProperties;
26import android.telephony.PhoneNumberUtils;
27import android.telephony.SmsMessage;
28import android.text.TextUtils;
29import android.telephony.Rlog;
30
31import com.android.internal.telephony.CommandsInterface;
32import com.android.internal.telephony.MccTable;
33import com.android.internal.telephony.SmsConstants;
34import com.android.internal.telephony.gsm.SimTlv;
35
36import java.io.FileDescriptor;
37import java.io.PrintWriter;
38import java.util.ArrayList;
39import java.util.Arrays;
40
41/**
42 * {@hide}
43 */
44public class SIMRecords extends IccRecords {
45    protected static final String LOG_TAG = "SIMRecords";
46
47    private static final boolean CRASH_RIL = false;
48
49    // ***** Instance Variables
50
51    VoiceMailConstants mVmConfig;
52
53
54    SpnOverride mSpnOverride;
55
56    // ***** Cached SIM State; cleared on channel close
57
58    private boolean mCallForwardingEnabled;
59
60
61    /**
62     * States only used by getSpnFsm FSM
63     */
64    private GetSpnFsmState mSpnState;
65
66    /** CPHS service information (See CPHS 4.2 B.3.1.1)
67     *  It will be set in onSimReady if reading GET_CPHS_INFO successfully
68     *  mCphsInfo[0] is CPHS Phase
69     *  mCphsInfo[1] and mCphsInfo[2] is CPHS Service Table
70     */
71    private byte[] mCphsInfo = null;
72    boolean mCspPlmnEnabled = true;
73
74    byte[] mEfMWIS = null;
75    byte[] mEfCPHS_MWI =null;
76    byte[] mEfCff = null;
77    byte[] mEfCfis = null;
78
79
80    int mSpnDisplayCondition;
81    // Numeric network codes listed in TS 51.011 EF[SPDI]
82    ArrayList<String> mSpdiNetworks = null;
83
84    String mPnnHomeName = null;
85
86    UsimServiceTable mUsimServiceTable;
87
88    @Override
89    public String toString() {
90        return "SimRecords: " + super.toString()
91                + " mVmConfig" + mVmConfig
92                + " mSpnOverride=" + "mSpnOverride"
93                + " callForwardingEnabled=" + mCallForwardingEnabled
94                + " spnState=" + mSpnState
95                + " mCphsInfo=" + mCphsInfo
96                + " mCspPlmnEnabled=" + mCspPlmnEnabled
97                + " efMWIS=" + mEfMWIS
98                + " efCPHS_MWI=" + mEfCPHS_MWI
99                + " mEfCff=" + mEfCff
100                + " mEfCfis=" + mEfCfis
101                + " getOperatorNumeric=" + getOperatorNumeric();
102    }
103
104    // ***** Constants
105
106    // From TS 51.011 EF[SPDI] section
107    static final int TAG_SPDI = 0xA3;
108    static final int TAG_SPDI_PLMN_LIST = 0x80;
109
110    // Full Name IEI from TS 24.008
111    static final int TAG_FULL_NETWORK_NAME = 0x43;
112
113    // Short Name IEI from TS 24.008
114    static final int TAG_SHORT_NETWORK_NAME = 0x45;
115
116    // active CFF from CPHS 4.2 B.4.5
117    static final int CFF_UNCONDITIONAL_ACTIVE = 0x0a;
118    static final int CFF_UNCONDITIONAL_DEACTIVE = 0x05;
119    static final int CFF_LINE1_MASK = 0x0f;
120    static final int CFF_LINE1_RESET = 0xf0;
121
122    // CPHS Service Table (See CPHS 4.2 B.3.1)
123    private static final int CPHS_SST_MBN_MASK = 0x30;
124    private static final int CPHS_SST_MBN_ENABLED = 0x30;
125
126    // EF_CFIS related constants
127    // Spec reference TS 51.011 section 10.3.46.
128    private static final int CFIS_BCD_NUMBER_LENGTH_OFFSET = 2;
129    private static final int CFIS_TON_NPI_OFFSET = 3;
130    private static final int CFIS_ADN_CAPABILITY_ID_OFFSET = 14;
131    private static final int CFIS_ADN_EXTENSION_ID_OFFSET = 15;
132
133    // ***** Event Constants
134    private static final int EVENT_GET_IMSI_DONE = 3;
135    private static final int EVENT_GET_ICCID_DONE = 4;
136    private static final int EVENT_GET_MBI_DONE = 5;
137    private static final int EVENT_GET_MBDN_DONE = 6;
138    private static final int EVENT_GET_MWIS_DONE = 7;
139    private static final int EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE = 8;
140    protected static final int EVENT_GET_AD_DONE = 9; // Admin data on SIM
141    protected static final int EVENT_GET_MSISDN_DONE = 10;
142    private static final int EVENT_GET_CPHS_MAILBOX_DONE = 11;
143    private static final int EVENT_GET_SPN_DONE = 12;
144    private static final int EVENT_GET_SPDI_DONE = 13;
145    private static final int EVENT_UPDATE_DONE = 14;
146    private static final int EVENT_GET_PNN_DONE = 15;
147    protected static final int EVENT_GET_SST_DONE = 17;
148    private static final int EVENT_GET_ALL_SMS_DONE = 18;
149    private static final int EVENT_MARK_SMS_READ_DONE = 19;
150    private static final int EVENT_SET_MBDN_DONE = 20;
151    private static final int EVENT_SMS_ON_SIM = 21;
152    private static final int EVENT_GET_SMS_DONE = 22;
153    private static final int EVENT_GET_CFF_DONE = 24;
154    private static final int EVENT_SET_CPHS_MAILBOX_DONE = 25;
155    private static final int EVENT_GET_INFO_CPHS_DONE = 26;
156    // private static final int EVENT_SET_MSISDN_DONE = 30; Defined in IccRecords as 30
157    private static final int EVENT_SIM_REFRESH = 31;
158    private static final int EVENT_GET_CFIS_DONE = 32;
159    private static final int EVENT_GET_CSP_CPHS_DONE = 33;
160    private static final int EVENT_GET_GID1_DONE = 34;
161
162    // Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length.
163
164    private static final String[] MCCMNC_CODES_HAVING_3DIGITS_MNC = {
165        "302370", "302720", "310260",
166        "405025", "405026", "405027", "405028", "405029", "405030", "405031", "405032",
167        "405033", "405034", "405035", "405036", "405037", "405038", "405039", "405040",
168        "405041", "405042", "405043", "405044", "405045", "405046", "405047", "405750",
169        "405751", "405752", "405753", "405754", "405755", "405756", "405799", "405800",
170        "405801", "405802", "405803", "405804", "405805", "405806", "405807", "405808",
171        "405809", "405810", "405811", "405812", "405813", "405814", "405815", "405816",
172        "405817", "405818", "405819", "405820", "405821", "405822", "405823", "405824",
173        "405825", "405826", "405827", "405828", "405829", "405830", "405831", "405832",
174        "405833", "405834", "405835", "405836", "405837", "405838", "405839", "405840",
175        "405841", "405842", "405843", "405844", "405845", "405846", "405847", "405848",
176        "405849", "405850", "405851", "405852", "405853", "405875", "405876", "405877",
177        "405878", "405879", "405880", "405881", "405882", "405883", "405884", "405885",
178        "405886", "405908", "405909", "405910", "405911", "405912", "405913", "405914",
179        "405915", "405916", "405917", "405918", "405919", "405920", "405921", "405922",
180        "405923", "405924", "405925", "405926", "405927", "405928", "405929", "405930",
181        "405931", "405932", "502142", "502143", "502145", "502146", "502147", "502148"
182    };
183
184    // ***** Constructor
185
186    public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
187        super(app, c, ci);
188
189        mAdnCache = new AdnRecordCache(mFh);
190
191        mVmConfig = new VoiceMailConstants();
192        mSpnOverride = new SpnOverride();
193
194        mRecordsRequested = false;  // No load request is made till SIM ready
195
196        // recordsToLoad is set to 0 because no requests are made yet
197        mRecordsToLoad = 0;
198
199        mCi.setOnSmsOnSim(this, EVENT_SMS_ON_SIM, null);
200        mCi.registerForIccRefresh(this, EVENT_SIM_REFRESH, null);
201
202        // Start off by setting empty state
203        resetRecords();
204        mParentApp.registerForReady(this, EVENT_APP_READY, null);
205        if (DBG) log("SIMRecords X ctor this=" + this);
206    }
207
208    @Override
209    public void dispose() {
210        if (DBG) log("Disposing SIMRecords this=" + this);
211        //Unregister for all events
212        mCi.unregisterForIccRefresh(this);
213        mCi.unSetOnSmsOnSim(this);
214        mParentApp.unregisterForReady(this);
215        resetRecords();
216        super.dispose();
217    }
218
219    @Override
220    protected void finalize() {
221        if(DBG) log("finalized");
222    }
223
224    protected void resetRecords() {
225        mImsi = null;
226        mMsisdn = null;
227        mVoiceMailNum = null;
228        mCountVoiceMessages = 0;
229        mMncLength = UNINITIALIZED;
230        mIccId = null;
231        // -1 means no EF_SPN found; treat accordingly.
232        mSpnDisplayCondition = -1;
233        mEfMWIS = null;
234        mEfCPHS_MWI = null;
235        mSpdiNetworks = null;
236        mPnnHomeName = null;
237        mGid1 = null;
238
239        mAdnCache.reset();
240
241        log("SIMRecords: onRadioOffOrNotAvailable set 'gsm.sim.operator.numeric' to operator=null");
242        SystemProperties.set(PROPERTY_ICC_OPERATOR_NUMERIC, null);
243        SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, null);
244        SystemProperties.set(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, null);
245
246        // recordsRequested is set to false indicating that the SIM
247        // read requests made so far are not valid. This is set to
248        // true only when fresh set of read requests are made.
249        mRecordsRequested = false;
250    }
251
252
253    //***** Public Methods
254
255    /**
256     * {@inheritDoc}
257     */
258    @Override
259    public String getIMSI() {
260        return mImsi;
261    }
262
263    @Override
264    public String getMsisdnNumber() {
265        return mMsisdn;
266    }
267
268    @Override
269    public String getGid1() {
270        return mGid1;
271    }
272
273    @Override
274    public UsimServiceTable getUsimServiceTable() {
275        return mUsimServiceTable;
276    }
277
278    /**
279     * Set subscriber number to SIM record
280     *
281     * The subscriber number is stored in EF_MSISDN (TS 51.011)
282     *
283     * When the operation is complete, onComplete will be sent to its handler
284     *
285     * @param alphaTag alpha-tagging of the dailing nubmer (up to 10 characters)
286     * @param number dailing nubmer (up to 20 digits)
287     *        if the number starts with '+', then set to international TOA
288     * @param onComplete
289     *        onComplete.obj will be an AsyncResult
290     *        ((AsyncResult)onComplete.obj).exception == null on success
291     *        ((AsyncResult)onComplete.obj).exception != null on fail
292     */
293    @Override
294    public void setMsisdnNumber(String alphaTag, String number,
295            Message onComplete) {
296
297        mMsisdn = number;
298        mMsisdnTag = alphaTag;
299
300        if(DBG) log("Set MSISDN: " + mMsisdnTag + " " + /*mMsisdn*/ "xxxxxxx");
301
302
303        AdnRecord adn = new AdnRecord(mMsisdnTag, mMsisdn);
304
305        new AdnRecordLoader(mFh).updateEF(adn, EF_MSISDN, EF_EXT1, 1, null,
306                obtainMessage(EVENT_SET_MSISDN_DONE, onComplete));
307    }
308
309    @Override
310    public String getMsisdnAlphaTag() {
311        return mMsisdnTag;
312    }
313
314    @Override
315    public String getVoiceMailNumber() {
316        return mVoiceMailNum;
317    }
318
319    /**
320     * Set voice mail number to SIM record
321     *
322     * The voice mail number can be stored either in EF_MBDN (TS 51.011) or
323     * EF_MAILBOX_CPHS (CPHS 4.2)
324     *
325     * If EF_MBDN is available, store the voice mail number to EF_MBDN
326     *
327     * If EF_MAILBOX_CPHS is enabled, store the voice mail number to EF_CHPS
328     *
329     * So the voice mail number will be stored in both EFs if both are available
330     *
331     * Return error only if both EF_MBDN and EF_MAILBOX_CPHS fail.
332     *
333     * When the operation is complete, onComplete will be sent to its handler
334     *
335     * @param alphaTag alpha-tagging of the dailing nubmer (upto 10 characters)
336     * @param voiceNumber dailing nubmer (upto 20 digits)
337     *        if the number is start with '+', then set to international TOA
338     * @param onComplete
339     *        onComplete.obj will be an AsyncResult
340     *        ((AsyncResult)onComplete.obj).exception == null on success
341     *        ((AsyncResult)onComplete.obj).exception != null on fail
342     */
343    @Override
344    public void setVoiceMailNumber(String alphaTag, String voiceNumber,
345            Message onComplete) {
346        if (mIsVoiceMailFixed) {
347            AsyncResult.forMessage((onComplete)).exception =
348                    new IccVmFixedException("Voicemail number is fixed by operator");
349            onComplete.sendToTarget();
350            return;
351        }
352
353        mNewVoiceMailNum = voiceNumber;
354        mNewVoiceMailTag = alphaTag;
355
356        AdnRecord adn = new AdnRecord(mNewVoiceMailTag, mNewVoiceMailNum);
357
358        if (mMailboxIndex != 0 && mMailboxIndex != 0xff) {
359
360            new AdnRecordLoader(mFh).updateEF(adn, EF_MBDN, EF_EXT6,
361                    mMailboxIndex, null,
362                    obtainMessage(EVENT_SET_MBDN_DONE, onComplete));
363
364        } else if (isCphsMailboxEnabled()) {
365
366            new AdnRecordLoader(mFh).updateEF(adn, EF_MAILBOX_CPHS,
367                    EF_EXT1, 1, null,
368                    obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE, onComplete));
369
370        } else {
371            AsyncResult.forMessage((onComplete)).exception =
372                    new IccVmNotSupportedException("Update SIM voice mailbox error");
373            onComplete.sendToTarget();
374        }
375    }
376
377    @Override
378    public String getVoiceMailAlphaTag()
379    {
380        return mVoiceMailTag;
381    }
382
383    /**
384     * Sets the SIM voice message waiting indicator records
385     * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported
386     * @param countWaiting The number of messages waiting, if known. Use
387     *                     -1 to indicate that an unknown number of
388     *                      messages are waiting
389     */
390    @Override
391    public void
392    setVoiceMessageWaiting(int line, int countWaiting) {
393        if (line != 1) {
394            // only profile 1 is supported
395            return;
396        }
397
398        // range check
399        if (countWaiting < 0) {
400            countWaiting = -1;
401        } else if (countWaiting > 0xff) {
402            // TS 23.040 9.2.3.24.2
403            // "The value 255 shall be taken to mean 255 or greater"
404            countWaiting = 0xff;
405        }
406
407        mCountVoiceMessages = countWaiting;
408
409        mRecordsEventsRegistrants.notifyResult(EVENT_MWI);
410
411        try {
412            if (mEfMWIS != null) {
413                // TS 51.011 10.3.45
414
415                // lsb of byte 0 is 'voicemail' status
416                mEfMWIS[0] = (byte)((mEfMWIS[0] & 0xfe)
417                                    | (mCountVoiceMessages == 0 ? 0 : 1));
418
419                // byte 1 is the number of voice messages waiting
420                if (countWaiting < 0) {
421                    // The spec does not define what this should be
422                    // if we don't know the count
423                    mEfMWIS[1] = 0;
424                } else {
425                    mEfMWIS[1] = (byte) countWaiting;
426                }
427
428                mFh.updateEFLinearFixed(
429                    EF_MWIS, 1, mEfMWIS, null,
430                    obtainMessage (EVENT_UPDATE_DONE, EF_MWIS));
431            }
432
433            if (mEfCPHS_MWI != null) {
434                    // Refer CPHS4_2.WW6 B4.2.3
435                mEfCPHS_MWI[0] = (byte)((mEfCPHS_MWI[0] & 0xf0)
436                            | (mCountVoiceMessages == 0 ? 0x5 : 0xa));
437
438                mFh.updateEFTransparent(
439                    EF_VOICE_MAIL_INDICATOR_CPHS, mEfCPHS_MWI,
440                    obtainMessage (EVENT_UPDATE_DONE, EF_VOICE_MAIL_INDICATOR_CPHS));
441            }
442        } catch (ArrayIndexOutOfBoundsException ex) {
443            logw("Error saving voice mail state to SIM. Probably malformed SIM record", ex);
444        }
445    }
446
447    // Validate data is !null and the MSP (Multiple Subscriber Profile)
448    // byte is between 1 and 4. See ETSI TS 131 102 v11.3.0 section 4.2.64.
449    private boolean validEfCfis(byte[] data) {
450        return ((data != null) && (data[0] >= 1) && (data[0] <= 4));
451    }
452
453    /**
454     * {@inheritDoc}
455     */
456    @Override
457    public boolean getVoiceCallForwardingFlag() {
458        return mCallForwardingEnabled;
459    }
460
461    /**
462     * {@inheritDoc}
463     */
464    @Override
465    public void setVoiceCallForwardingFlag(int line, boolean enable, String dialNumber) {
466
467        if (line != 1) return; // only line 1 is supported
468
469        mCallForwardingEnabled = enable;
470
471        mRecordsEventsRegistrants.notifyResult(EVENT_CFI);
472
473        try {
474            if (validEfCfis(mEfCfis)) {
475                // lsb is of byte 1 is voice status
476                if (enable) {
477                    mEfCfis[1] |= 1;
478                } else {
479                    mEfCfis[1] &= 0xfe;
480                }
481
482                log("setVoiceCallForwardingFlag: enable=" + enable
483                        + " mEfCfis=" + IccUtils.bytesToHexString(mEfCfis));
484
485                // Update dialNumber if not empty and CFU is enabled.
486                // Spec reference for EF_CFIS contents, TS 51.011 section 10.3.46.
487                if (enable && !TextUtils.isEmpty(dialNumber)) {
488                    log("EF_CFIS: updating cf number, " + dialNumber);
489                    byte[] bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(dialNumber);
490
491                    System.arraycopy(bcdNumber, 0, mEfCfis, CFIS_TON_NPI_OFFSET, bcdNumber.length);
492
493                    mEfCfis[CFIS_BCD_NUMBER_LENGTH_OFFSET] = (byte) (bcdNumber.length);
494                    mEfCfis[CFIS_ADN_CAPABILITY_ID_OFFSET] = (byte) 0xFF;
495                    mEfCfis[CFIS_ADN_EXTENSION_ID_OFFSET] = (byte) 0xFF;
496                }
497
498                mFh.updateEFLinearFixed(
499                        EF_CFIS, 1, mEfCfis, null,
500                        obtainMessage (EVENT_UPDATE_DONE, EF_CFIS));
501            } else {
502                log("setVoiceCallForwardingFlag: ignoring enable=" + enable
503                        + " invalid mEfCfis=" + IccUtils.bytesToHexString(mEfCfis));
504            }
505
506            if (mEfCff != null) {
507                if (enable) {
508                    mEfCff[0] = (byte) ((mEfCff[0] & CFF_LINE1_RESET)
509                            | CFF_UNCONDITIONAL_ACTIVE);
510                } else {
511                    mEfCff[0] = (byte) ((mEfCff[0] & CFF_LINE1_RESET)
512                            | CFF_UNCONDITIONAL_DEACTIVE);
513                }
514
515                mFh.updateEFTransparent(
516                        EF_CFF_CPHS, mEfCff,
517                        obtainMessage (EVENT_UPDATE_DONE, EF_CFF_CPHS));
518            }
519        } catch (ArrayIndexOutOfBoundsException ex) {
520            logw("Error saving call forwarding flag to SIM. "
521                            + "Probably malformed SIM record", ex);
522
523        }
524    }
525
526    /**
527     * Called by STK Service when REFRESH is received.
528     * @param fileChanged indicates whether any files changed
529     * @param fileList if non-null, a list of EF files that changed
530     */
531    @Override
532    public void onRefresh(boolean fileChanged, int[] fileList) {
533        if (fileChanged) {
534            // A future optimization would be to inspect fileList and
535            // only reload those files that we care about.  For now,
536            // just re-fetch all SIM records that we cache.
537            fetchSimRecords();
538        }
539    }
540
541    /**
542     * {@inheritDoc}
543     */
544    @Override
545    public String getOperatorNumeric() {
546        if (mImsi == null) {
547            log("getOperatorNumeric: IMSI == null");
548            return null;
549        }
550        if (mMncLength == UNINITIALIZED || mMncLength == UNKNOWN) {
551            log("getSIMOperatorNumeric: bad mncLength");
552            return null;
553        }
554
555        // Length = length of MCC + length of MNC
556        // length of mcc = 3 (TS 23.003 Section 2.2)
557        return mImsi.substring(0, 3 + mMncLength);
558    }
559
560    // ***** Overridden from Handler
561    @Override
562    public void handleMessage(Message msg) {
563        AsyncResult ar;
564        AdnRecord adn;
565
566        byte data[];
567
568        boolean isRecordLoadResponse = false;
569
570        if (mDestroyed.get()) {
571            loge("Received message " + msg + "[" + msg.what + "] " +
572                    " while being destroyed. Ignoring.");
573            return;
574        }
575
576        try { switch (msg.what) {
577            case EVENT_APP_READY:
578                onReady();
579                break;
580
581            /* IO events */
582            case EVENT_GET_IMSI_DONE:
583                isRecordLoadResponse = true;
584
585                ar = (AsyncResult)msg.obj;
586
587                if (ar.exception != null) {
588                    loge("Exception querying IMSI, Exception:" + ar.exception);
589                    break;
590                }
591
592                mImsi = (String) ar.result;
593
594                // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more
595                // than 15 (and usually 15).
596                if (mImsi != null && (mImsi.length() < 6 || mImsi.length() > 15)) {
597                    loge("invalid IMSI " + mImsi);
598                    mImsi = null;
599                }
600
601                log("IMSI: " + /* imsi.substring(0, 6) +*/ "xxxxxxx");
602
603                if (((mMncLength == UNKNOWN) || (mMncLength == 2)) &&
604                        ((mImsi != null) && (mImsi.length() >= 6))) {
605                    String mccmncCode = mImsi.substring(0, 6);
606                    for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
607                        if (mccmnc.equals(mccmncCode)) {
608                            mMncLength = 3;
609                            break;
610                        }
611                    }
612                }
613
614                if (mMncLength == UNKNOWN) {
615                    // the SIM has told us all it knows, but it didn't know the mnc length.
616                    // guess using the mcc
617                    try {
618                        int mcc = Integer.parseInt(mImsi.substring(0,3));
619                        mMncLength = MccTable.smallestDigitsMccForMnc(mcc);
620                    } catch (NumberFormatException e) {
621                        mMncLength = UNKNOWN;
622                        loge("Corrupt IMSI!");
623                    }
624                }
625
626                if (mMncLength != UNKNOWN && mMncLength != UNINITIALIZED) {
627                    // finally have both the imsi and the mncLength and can parse the imsi properly
628                    MccTable.updateMccMncConfiguration(mContext, mImsi.substring(0, 3 + mMncLength));
629                }
630                mImsiReadyRegistrants.notifyRegistrants();
631            break;
632
633            case EVENT_GET_MBI_DONE:
634                boolean isValidMbdn;
635                isRecordLoadResponse = true;
636
637                ar = (AsyncResult)msg.obj;
638                data = (byte[]) ar.result;
639
640                isValidMbdn = false;
641                if (ar.exception == null) {
642                    // Refer TS 51.011 Section 10.3.44 for content details
643                    log("EF_MBI: " + IccUtils.bytesToHexString(data));
644
645                    // Voice mail record number stored first
646                    mMailboxIndex = data[0] & 0xff;
647
648                    // check if dailing numbe id valid
649                    if (mMailboxIndex != 0 && mMailboxIndex != 0xff) {
650                        log("Got valid mailbox number for MBDN");
651                        isValidMbdn = true;
652                    }
653                }
654
655                // one more record to load
656                mRecordsToLoad += 1;
657
658                if (isValidMbdn) {
659                    // Note: MBDN was not included in NUM_OF_SIM_RECORDS_LOADED
660                    new AdnRecordLoader(mFh).loadFromEF(EF_MBDN, EF_EXT6,
661                            mMailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE));
662                } else {
663                    // If this EF not present, try mailbox as in CPHS standard
664                    // CPHS (CPHS4_2.WW6) is a european standard.
665                    new AdnRecordLoader(mFh).loadFromEF(EF_MAILBOX_CPHS,
666                            EF_EXT1, 1,
667                            obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
668                }
669
670                break;
671            case EVENT_GET_CPHS_MAILBOX_DONE:
672            case EVENT_GET_MBDN_DONE:
673                //Resetting the voice mail number and voice mail tag to null
674                //as these should be updated from the data read from EF_MBDN.
675                //If they are not reset, incase of invalid data/exception these
676                //variables are retaining their previous values and are
677                //causing invalid voice mailbox info display to user.
678                mVoiceMailNum = null;
679                mVoiceMailTag = null;
680                isRecordLoadResponse = true;
681
682                ar = (AsyncResult)msg.obj;
683
684                if (ar.exception != null) {
685
686                    log("Invalid or missing EF"
687                        + ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? "[MAILBOX]" : "[MBDN]"));
688
689                    // Bug #645770 fall back to CPHS
690                    // FIXME should use SST to decide
691
692                    if (msg.what == EVENT_GET_MBDN_DONE) {
693                        //load CPHS on fail...
694                        // FIXME right now, only load line1's CPHS voice mail entry
695
696                        mRecordsToLoad += 1;
697                        new AdnRecordLoader(mFh).loadFromEF(
698                                EF_MAILBOX_CPHS, EF_EXT1, 1,
699                                obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
700                    }
701                    break;
702                }
703
704                adn = (AdnRecord)ar.result;
705
706                log("VM: " + adn +
707                        ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? " EF[MAILBOX]" : " EF[MBDN]"));
708
709                if (adn.isEmpty() && msg.what == EVENT_GET_MBDN_DONE) {
710                    // Bug #645770 fall back to CPHS
711                    // FIXME should use SST to decide
712                    // FIXME right now, only load line1's CPHS voice mail entry
713                    mRecordsToLoad += 1;
714                    new AdnRecordLoader(mFh).loadFromEF(
715                            EF_MAILBOX_CPHS, EF_EXT1, 1,
716                            obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
717
718                    break;
719                }
720
721                mVoiceMailNum = adn.getNumber();
722                mVoiceMailTag = adn.getAlphaTag();
723            break;
724
725            case EVENT_GET_MSISDN_DONE:
726                isRecordLoadResponse = true;
727
728                ar = (AsyncResult)msg.obj;
729
730                if (ar.exception != null) {
731                    log("Invalid or missing EF[MSISDN]");
732                    break;
733                }
734
735                adn = (AdnRecord)ar.result;
736
737                mMsisdn = adn.getNumber();
738                mMsisdnTag = adn.getAlphaTag();
739
740                log("MSISDN: " + /*mMsisdn*/ "xxxxxxx");
741            break;
742
743            case EVENT_SET_MSISDN_DONE:
744                isRecordLoadResponse = false;
745                ar = (AsyncResult)msg.obj;
746
747                if (ar.userObj != null) {
748                    AsyncResult.forMessage(((Message) ar.userObj)).exception
749                            = ar.exception;
750                    ((Message) ar.userObj).sendToTarget();
751                }
752                break;
753
754            case EVENT_GET_MWIS_DONE:
755                isRecordLoadResponse = true;
756
757                ar = (AsyncResult)msg.obj;
758                data = (byte[])ar.result;
759
760                if (ar.exception != null) {
761                    break;
762                }
763
764                log("EF_MWIS: " + IccUtils.bytesToHexString(data));
765
766                mEfMWIS = data;
767
768                if ((data[0] & 0xff) == 0xff) {
769                    log("Uninitialized record MWIS");
770                    break;
771                }
772
773                // Refer TS 51.011 Section 10.3.45 for the content description
774                boolean voiceMailWaiting = ((data[0] & 0x01) != 0);
775                mCountVoiceMessages = data[1] & 0xff;
776
777                if (voiceMailWaiting && mCountVoiceMessages == 0) {
778                    // Unknown count = -1
779                    mCountVoiceMessages = -1;
780                }
781
782                mRecordsEventsRegistrants.notifyResult(EVENT_MWI);
783            break;
784
785            case EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE:
786                isRecordLoadResponse = true;
787
788                ar = (AsyncResult)msg.obj;
789                data = (byte[])ar.result;
790
791                if (ar.exception != null) {
792                    break;
793                }
794
795                mEfCPHS_MWI = data;
796
797                // Use this data if the EF[MWIS] exists and
798                // has been loaded
799
800                if (mEfMWIS == null) {
801                    int indicator = data[0] & 0xf;
802
803                    // Refer CPHS4_2.WW6 B4.2.3
804                    if (indicator == 0xA) {
805                        // Unknown count = -1
806                        mCountVoiceMessages = -1;
807                    } else if (indicator == 0x5) {
808                        mCountVoiceMessages = 0;
809                    }
810
811                    mRecordsEventsRegistrants.notifyResult(EVENT_MWI);
812                }
813            break;
814
815            case EVENT_GET_ICCID_DONE:
816                isRecordLoadResponse = true;
817
818                ar = (AsyncResult)msg.obj;
819                data = (byte[])ar.result;
820
821                if (ar.exception != null) {
822                    break;
823                }
824
825                mIccId = IccUtils.bcdToString(data, 0, data.length);
826
827                log("iccid: " + mIccId);
828
829            break;
830
831
832            case EVENT_GET_AD_DONE:
833                try {
834                    isRecordLoadResponse = true;
835
836                    ar = (AsyncResult)msg.obj;
837                    data = (byte[])ar.result;
838
839                    if (ar.exception != null) {
840                        break;
841                    }
842
843                    log("EF_AD: " + IccUtils.bytesToHexString(data));
844
845                    if (data.length < 3) {
846                        log("Corrupt AD data on SIM");
847                        break;
848                    }
849
850                    if (data.length == 3) {
851                        log("MNC length not present in EF_AD");
852                        break;
853                    }
854
855                    mMncLength = data[3] & 0xf;
856
857                    if (mMncLength == 0xf) {
858                        mMncLength = UNKNOWN;
859                    }
860                } finally {
861                    if (((mMncLength == UNINITIALIZED) || (mMncLength == UNKNOWN) ||
862                            (mMncLength == 2)) && ((mImsi != null) && (mImsi.length() >= 6))) {
863                        String mccmncCode = mImsi.substring(0, 6);
864                        for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
865                            if (mccmnc.equals(mccmncCode)) {
866                                mMncLength = 3;
867                                break;
868                            }
869                        }
870                    }
871
872                    if (mMncLength == UNKNOWN || mMncLength == UNINITIALIZED) {
873                        if (mImsi != null) {
874                            try {
875                                int mcc = Integer.parseInt(mImsi.substring(0,3));
876
877                                mMncLength = MccTable.smallestDigitsMccForMnc(mcc);
878                            } catch (NumberFormatException e) {
879                                mMncLength = UNKNOWN;
880                                loge("Corrupt IMSI!");
881                            }
882                        } else {
883                            // Indicate we got this info, but it didn't contain the length.
884                            mMncLength = UNKNOWN;
885
886                            log("MNC length not present in EF_AD");
887                        }
888                    }
889                    if (mImsi != null && mMncLength != UNKNOWN) {
890                        // finally have both imsi and the length of the mnc and can parse
891                        // the imsi properly
892                        MccTable.updateMccMncConfiguration(mContext,
893                                mImsi.substring(0, 3 + mMncLength));
894                    }
895                }
896            break;
897
898            case EVENT_GET_SPN_DONE:
899                isRecordLoadResponse = true;
900                ar = (AsyncResult) msg.obj;
901                getSpnFsm(false, ar);
902            break;
903
904            case EVENT_GET_CFF_DONE:
905                isRecordLoadResponse = true;
906
907                ar = (AsyncResult) msg.obj;
908                data = (byte[]) ar.result;
909
910                if (ar.exception != null) {
911                    break;
912                }
913
914                log("EF_CFF_CPHS: " + IccUtils.bytesToHexString(data));
915                mEfCff = data;
916
917                // if EF_CFIS is valid, prefer it to EF_CFF_CPHS
918                if (!validEfCfis(mEfCfis)) {
919                    mCallForwardingEnabled =
920                        ((data[0] & CFF_LINE1_MASK) == CFF_UNCONDITIONAL_ACTIVE);
921
922                    mRecordsEventsRegistrants.notifyResult(EVENT_CFI);
923                } else {
924                    log("EVENT_GET_CFF_DONE: EF_CFIS is valid, ignoring EF_CFF_CPHS");
925                }
926                break;
927
928            case EVENT_GET_SPDI_DONE:
929                isRecordLoadResponse = true;
930
931                ar = (AsyncResult)msg.obj;
932                data = (byte[])ar.result;
933
934                if (ar.exception != null) {
935                    break;
936                }
937
938                parseEfSpdi(data);
939            break;
940
941            case EVENT_UPDATE_DONE:
942                ar = (AsyncResult)msg.obj;
943                if (ar.exception != null) {
944                    logw("update failed. ", ar.exception);
945                }
946            break;
947
948            case EVENT_GET_PNN_DONE:
949                isRecordLoadResponse = true;
950
951                ar = (AsyncResult)msg.obj;
952                data = (byte[])ar.result;
953
954                if (ar.exception != null) {
955                    break;
956                }
957
958                SimTlv tlv = new SimTlv(data, 0, data.length);
959
960                for ( ; tlv.isValidObject() ; tlv.nextObject()) {
961                    if (tlv.getTag() == TAG_FULL_NETWORK_NAME) {
962                        mPnnHomeName
963                            = IccUtils.networkNameToString(
964                                tlv.getData(), 0, tlv.getData().length);
965                        break;
966                    }
967                }
968            break;
969
970            case EVENT_GET_ALL_SMS_DONE:
971                isRecordLoadResponse = true;
972
973                ar = (AsyncResult)msg.obj;
974                if (ar.exception != null)
975                    break;
976
977                handleSmses((ArrayList<byte []>) ar.result);
978                break;
979
980            case EVENT_MARK_SMS_READ_DONE:
981                Rlog.i("ENF", "marked read: sms " + msg.arg1);
982                break;
983
984
985            case EVENT_SMS_ON_SIM:
986                isRecordLoadResponse = false;
987
988                ar = (AsyncResult)msg.obj;
989
990                int[] index = (int[])ar.result;
991
992                if (ar.exception != null || index.length != 1) {
993                    loge("Error on SMS_ON_SIM with exp "
994                            + ar.exception + " length " + index.length);
995                } else {
996                    log("READ EF_SMS RECORD index=" + index[0]);
997                    mFh.loadEFLinearFixed(EF_SMS,index[0],
998                            obtainMessage(EVENT_GET_SMS_DONE));
999                }
1000                break;
1001
1002            case EVENT_GET_SMS_DONE:
1003                isRecordLoadResponse = false;
1004                ar = (AsyncResult)msg.obj;
1005                if (ar.exception == null) {
1006                    handleSms((byte[])ar.result);
1007                } else {
1008                    loge("Error on GET_SMS with exp " + ar.exception);
1009                }
1010                break;
1011            case EVENT_GET_SST_DONE:
1012                isRecordLoadResponse = true;
1013
1014                ar = (AsyncResult)msg.obj;
1015                data = (byte[])ar.result;
1016
1017                if (ar.exception != null) {
1018                    break;
1019                }
1020
1021                mUsimServiceTable = new UsimServiceTable(data);
1022                if (DBG) log("SST: " + mUsimServiceTable);
1023                break;
1024
1025            case EVENT_GET_INFO_CPHS_DONE:
1026                isRecordLoadResponse = true;
1027
1028                ar = (AsyncResult)msg.obj;
1029
1030                if (ar.exception != null) {
1031                    break;
1032                }
1033
1034                mCphsInfo = (byte[])ar.result;
1035
1036                if (DBG) log("iCPHS: " + IccUtils.bytesToHexString(mCphsInfo));
1037            break;
1038
1039            case EVENT_SET_MBDN_DONE:
1040                isRecordLoadResponse = false;
1041                ar = (AsyncResult)msg.obj;
1042
1043                if (ar.exception == null) {
1044                    mVoiceMailNum = mNewVoiceMailNum;
1045                    mVoiceMailTag = mNewVoiceMailTag;
1046                }
1047
1048                if (isCphsMailboxEnabled()) {
1049                    adn = new AdnRecord(mVoiceMailTag, mVoiceMailNum);
1050                    Message onCphsCompleted = (Message) ar.userObj;
1051
1052                    /* write to cphs mailbox whenever it is available but
1053                    * we only need notify caller once if both updating are
1054                    * successful.
1055                    *
1056                    * so if set_mbdn successful, notify caller here and set
1057                    * onCphsCompleted to null
1058                    */
1059                    if (ar.exception == null && ar.userObj != null) {
1060                        AsyncResult.forMessage(((Message) ar.userObj)).exception
1061                                = null;
1062                        ((Message) ar.userObj).sendToTarget();
1063
1064                        if (DBG) log("Callback with MBDN successful.");
1065
1066                        onCphsCompleted = null;
1067                    }
1068
1069                    new AdnRecordLoader(mFh).
1070                            updateEF(adn, EF_MAILBOX_CPHS, EF_EXT1, 1, null,
1071                            obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE,
1072                                    onCphsCompleted));
1073                } else {
1074                    if (ar.userObj != null) {
1075                        AsyncResult.forMessage(((Message) ar.userObj)).exception
1076                                = ar.exception;
1077                        ((Message) ar.userObj).sendToTarget();
1078                    }
1079                }
1080                break;
1081            case EVENT_SET_CPHS_MAILBOX_DONE:
1082                isRecordLoadResponse = false;
1083                ar = (AsyncResult)msg.obj;
1084                if(ar.exception == null) {
1085                    mVoiceMailNum = mNewVoiceMailNum;
1086                    mVoiceMailTag = mNewVoiceMailTag;
1087                } else {
1088                    if (DBG) log("Set CPHS MailBox with exception: "
1089                            + ar.exception);
1090                }
1091                if (ar.userObj != null) {
1092                    if (DBG) log("Callback with CPHS MB successful.");
1093                    AsyncResult.forMessage(((Message) ar.userObj)).exception
1094                            = ar.exception;
1095                    ((Message) ar.userObj).sendToTarget();
1096                }
1097                break;
1098            case EVENT_SIM_REFRESH:
1099                isRecordLoadResponse = false;
1100                ar = (AsyncResult)msg.obj;
1101                if (DBG) log("Sim REFRESH with exception: " + ar.exception);
1102                if (ar.exception == null) {
1103                    handleSimRefresh((IccRefreshResponse)ar.result);
1104                }
1105                break;
1106            case EVENT_GET_CFIS_DONE:
1107                isRecordLoadResponse = true;
1108
1109                ar = (AsyncResult)msg.obj;
1110                data = (byte[])ar.result;
1111
1112                if (ar.exception != null) {
1113                    break;
1114                }
1115
1116                log("EF_CFIS: " + IccUtils.bytesToHexString(data));
1117
1118                if (validEfCfis(data)) {
1119                    mEfCfis = data;
1120
1121                    // Refer TS 51.011 Section 10.3.46 for the content description
1122                    mCallForwardingEnabled = ((data[1] & 0x01) != 0);
1123                    log("EF_CFIS: callForwardingEnabled=" + mCallForwardingEnabled);
1124
1125                    mRecordsEventsRegistrants.notifyResult(EVENT_CFI);
1126                } else {
1127                    log("EF_CFIS: invalid data=" + IccUtils.bytesToHexString(data));
1128                }
1129                break;
1130
1131            case EVENT_GET_CSP_CPHS_DONE:
1132                isRecordLoadResponse = true;
1133
1134                ar = (AsyncResult)msg.obj;
1135
1136                if (ar.exception != null) {
1137                    loge("Exception in fetching EF_CSP data " + ar.exception);
1138                    break;
1139                }
1140
1141                data = (byte[])ar.result;
1142
1143                log("EF_CSP: " + IccUtils.bytesToHexString(data));
1144                handleEfCspData(data);
1145                break;
1146
1147            case EVENT_GET_GID1_DONE:
1148                isRecordLoadResponse = true;
1149
1150                ar = (AsyncResult)msg.obj;
1151                data =(byte[])ar.result;
1152
1153                if (ar.exception != null) {
1154                    loge("Exception in get GID1 " + ar.exception);
1155                    mGid1 = null;
1156                    break;
1157                }
1158                mGid1 = IccUtils.bytesToHexString(data);
1159                log("GID1: " + mGid1);
1160
1161                break;
1162
1163            default:
1164                super.handleMessage(msg);   // IccRecords handles generic record load responses
1165
1166        }}catch (RuntimeException exc) {
1167            // I don't want these exceptions to be fatal
1168            logw("Exception parsing SIM record", exc);
1169        } finally {
1170            // Count up record load responses even if they are fails
1171            if (isRecordLoadResponse) {
1172                onRecordLoaded();
1173            }
1174        }
1175    }
1176
1177    private void handleFileUpdate(int efid) {
1178        switch(efid) {
1179            case EF_MBDN:
1180                mRecordsToLoad++;
1181                new AdnRecordLoader(mFh).loadFromEF(EF_MBDN, EF_EXT6,
1182                        mMailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE));
1183                break;
1184            case EF_MAILBOX_CPHS:
1185                mRecordsToLoad++;
1186                new AdnRecordLoader(mFh).loadFromEF(EF_MAILBOX_CPHS, EF_EXT1,
1187                        1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
1188                break;
1189            case EF_CSP_CPHS:
1190                mRecordsToLoad++;
1191                log("[CSP] SIM Refresh for EF_CSP_CPHS");
1192                mFh.loadEFTransparent(EF_CSP_CPHS,
1193                        obtainMessage(EVENT_GET_CSP_CPHS_DONE));
1194                break;
1195            case EF_FDN:
1196                if (DBG) log("SIM Refresh called for EF_FDN");
1197                mParentApp.queryFdn();
1198                break;
1199            default:
1200                // For now, fetch all records if this is not a
1201                // voicemail number.
1202                // TODO: Handle other cases, instead of fetching all.
1203                mAdnCache.reset();
1204                fetchSimRecords();
1205                break;
1206        }
1207    }
1208
1209    private void handleSimRefresh(IccRefreshResponse refreshResponse){
1210        if (refreshResponse == null) {
1211            if (DBG) log("handleSimRefresh received without input");
1212            return;
1213        }
1214
1215        if (refreshResponse.aid != null &&
1216                !refreshResponse.aid.equals(mParentApp.getAid())) {
1217            // This is for different app. Ignore.
1218            return;
1219        }
1220
1221        switch (refreshResponse.refreshResult) {
1222            case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE:
1223                if (DBG) log("handleSimRefresh with SIM_FILE_UPDATED");
1224                handleFileUpdate(refreshResponse.efId);
1225                break;
1226            case IccRefreshResponse.REFRESH_RESULT_INIT:
1227                if (DBG) log("handleSimRefresh with SIM_REFRESH_INIT");
1228                // need to reload all files (that we care about)
1229                onIccRefreshInit();
1230                break;
1231            case IccRefreshResponse.REFRESH_RESULT_RESET:
1232                if (DBG) log("handleSimRefresh with SIM_REFRESH_RESET");
1233                mCi.setRadioPower(false, null);
1234                /* Note: no need to call setRadioPower(true).  Assuming the desired
1235                * radio power state is still ON (as tracked by ServiceStateTracker),
1236                * ServiceStateTracker will call setRadioPower when it receives the
1237                * RADIO_STATE_CHANGED notification for the power off.  And if the
1238                * desired power state has changed in the interim, we don't want to
1239                * override it with an unconditional power on.
1240                */
1241                break;
1242            default:
1243                // unknown refresh operation
1244                if (DBG) log("handleSimRefresh with unknown operation");
1245                break;
1246        }
1247    }
1248
1249    /**
1250     * Dispatch 3GPP format message to registrant ({@code GSMPhone} or {@code CDMALTEPhone})
1251     * to pass to the 3GPP SMS dispatcher for delivery.
1252     */
1253    private int dispatchGsmMessage(SmsMessage message) {
1254        mNewSmsRegistrants.notifyResult(message);
1255        return 0;
1256    }
1257
1258    private void handleSms(byte[] ba) {
1259        if (ba[0] != 0)
1260            Rlog.d("ENF", "status : " + ba[0]);
1261
1262        // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1263        // 3 == "received by MS from network; message to be read"
1264        if (ba[0] == 3) {
1265            int n = ba.length;
1266
1267            // Note: Data may include trailing FF's.  That's OK; message
1268            // should still parse correctly.
1269            byte[] pdu = new byte[n - 1];
1270            System.arraycopy(ba, 1, pdu, 0, n - 1);
1271            SmsMessage message = SmsMessage.createFromPdu(pdu, SmsConstants.FORMAT_3GPP);
1272
1273            dispatchGsmMessage(message);
1274        }
1275    }
1276
1277
1278    private void handleSmses(ArrayList<byte[]> messages) {
1279        int count = messages.size();
1280
1281        for (int i = 0; i < count; i++) {
1282            byte[] ba = messages.get(i);
1283
1284            if (ba[0] != 0)
1285                Rlog.i("ENF", "status " + i + ": " + ba[0]);
1286
1287            // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1288            // 3 == "received by MS from network; message to be read"
1289
1290            if (ba[0] == 3) {
1291                int n = ba.length;
1292
1293                // Note: Data may include trailing FF's.  That's OK; message
1294                // should still parse correctly.
1295                byte[] pdu = new byte[n - 1];
1296                System.arraycopy(ba, 1, pdu, 0, n - 1);
1297                SmsMessage message = SmsMessage.createFromPdu(pdu, SmsConstants.FORMAT_3GPP);
1298
1299                dispatchGsmMessage(message);
1300
1301                // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1302                // 1 == "received by MS from network; message read"
1303
1304                ba[0] = 1;
1305
1306                if (false) { // FIXME: writing seems to crash RdoServD
1307                    mFh.updateEFLinearFixed(EF_SMS,
1308                            i, ba, null, obtainMessage(EVENT_MARK_SMS_READ_DONE, i));
1309                }
1310            }
1311        }
1312    }
1313
1314    @Override
1315    protected void onRecordLoaded() {
1316        // One record loaded successfully or failed, In either case
1317        // we need to update the recordsToLoad count
1318        mRecordsToLoad -= 1;
1319        if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested);
1320
1321        if (mRecordsToLoad == 0 && mRecordsRequested == true) {
1322            onAllRecordsLoaded();
1323        } else if (mRecordsToLoad < 0) {
1324            loge("recordsToLoad <0, programmer error suspected");
1325            mRecordsToLoad = 0;
1326        }
1327    }
1328
1329    @Override
1330    protected void onAllRecordsLoaded() {
1331        if (DBG) log("record load complete");
1332
1333        // Some fields require more than one SIM record to set
1334
1335        String operator = getOperatorNumeric();
1336        if (!TextUtils.isEmpty(operator)) {
1337            log("onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" +
1338                    operator + "'");
1339            SystemProperties.set(PROPERTY_ICC_OPERATOR_NUMERIC, operator);
1340        } else {
1341            log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping");
1342        }
1343
1344        if (!TextUtils.isEmpty(mImsi)) {
1345            log("onAllRecordsLoaded set mcc imsi=" + mImsi);
1346            SystemProperties.set(PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
1347                    MccTable.countryCodeForMcc(Integer.parseInt(mImsi.substring(0,3))));
1348        } else {
1349            log("onAllRecordsLoaded empty imsi skipping setting mcc");
1350        }
1351
1352        setVoiceMailByCountry(operator);
1353        setSpnFromConfig(operator);
1354
1355        mRecordsLoadedRegistrants.notifyRegistrants(
1356            new AsyncResult(null, null, null));
1357    }
1358
1359    //***** Private methods
1360
1361    private void setSpnFromConfig(String carrier) {
1362        if (mSpnOverride.containsCarrier(carrier)) {
1363            mSpn = mSpnOverride.getSpn(carrier);
1364        }
1365    }
1366
1367
1368    private void setVoiceMailByCountry (String spn) {
1369        if (mVmConfig.containsCarrier(spn)) {
1370            mIsVoiceMailFixed = true;
1371            mVoiceMailNum = mVmConfig.getVoiceMailNumber(spn);
1372            mVoiceMailTag = mVmConfig.getVoiceMailTag(spn);
1373        }
1374    }
1375
1376    @Override
1377    public void onReady() {
1378        fetchSimRecords();
1379    }
1380
1381    protected void fetchSimRecords() {
1382        mRecordsRequested = true;
1383
1384        if (DBG) log("fetchSimRecords " + mRecordsToLoad);
1385
1386        mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));
1387        mRecordsToLoad++;
1388
1389        mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
1390        mRecordsToLoad++;
1391
1392        // FIXME should examine EF[MSISDN]'s capability configuration
1393        // to determine which is the voice/data/fax line
1394        new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, EF_EXT1, 1,
1395                    obtainMessage(EVENT_GET_MSISDN_DONE));
1396        mRecordsToLoad++;
1397
1398        // Record number is subscriber profile
1399        mFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE));
1400        mRecordsToLoad++;
1401
1402        mFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE));
1403        mRecordsToLoad++;
1404
1405        // Record number is subscriber profile
1406        mFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE));
1407        mRecordsToLoad++;
1408
1409
1410        // Also load CPHS-style voice mail indicator, which stores
1411        // the same info as EF[MWIS]. If both exist, both are updated
1412        // but the EF[MWIS] data is preferred
1413        // Please note this must be loaded after EF[MWIS]
1414        mFh.loadEFTransparent(
1415                EF_VOICE_MAIL_INDICATOR_CPHS,
1416                obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE));
1417        mRecordsToLoad++;
1418
1419        // Same goes for Call Forward Status indicator: fetch both
1420        // EF[CFIS] and CPHS-EF, with EF[CFIS] preferred.
1421        mFh.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE));
1422        mRecordsToLoad++;
1423        mFh.loadEFTransparent(EF_CFF_CPHS, obtainMessage(EVENT_GET_CFF_DONE));
1424        mRecordsToLoad++;
1425
1426
1427        getSpnFsm(true, null);
1428
1429        mFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE));
1430        mRecordsToLoad++;
1431
1432        mFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE));
1433        mRecordsToLoad++;
1434
1435        mFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE));
1436        mRecordsToLoad++;
1437
1438        mFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE));
1439        mRecordsToLoad++;
1440
1441        mFh.loadEFTransparent(EF_CSP_CPHS,obtainMessage(EVENT_GET_CSP_CPHS_DONE));
1442        mRecordsToLoad++;
1443
1444        mFh.loadEFTransparent(EF_GID1, obtainMessage(EVENT_GET_GID1_DONE));
1445        mRecordsToLoad++;
1446
1447        // XXX should seek instead of examining them all
1448        if (false) { // XXX
1449            mFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE));
1450            mRecordsToLoad++;
1451        }
1452
1453        if (CRASH_RIL) {
1454            String sms = "0107912160130310f20404d0110041007030208054832b0120"
1455                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1456                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1457                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1458                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1459                         + "ffffffffffffffffffffffffffffff";
1460            byte[] ba = IccUtils.hexStringToBytes(sms);
1461
1462            mFh.updateEFLinearFixed(EF_SMS, 1, ba, null,
1463                            obtainMessage(EVENT_MARK_SMS_READ_DONE, 1));
1464        }
1465        if (DBG) log("fetchSimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested);
1466    }
1467
1468    /**
1469     * Returns the SpnDisplayRule based on settings on the SIM and the
1470     * specified plmn (currently-registered PLMN).  See TS 22.101 Annex A
1471     * and TS 51.011 10.3.11 for details.
1472     *
1473     * If the SPN is not found on the SIM or is empty, the rule is
1474     * always PLMN_ONLY.
1475     */
1476    @Override
1477    public int getDisplayRule(String plmn) {
1478        int rule;
1479        if (TextUtils.isEmpty(mSpn) || mSpnDisplayCondition == -1) {
1480            // No EF_SPN content was found on the SIM, or not yet loaded.  Just show ONS.
1481            rule = SPN_RULE_SHOW_PLMN;
1482        } else if (isOnMatchingPlmn(plmn)) {
1483            rule = SPN_RULE_SHOW_SPN;
1484            if ((mSpnDisplayCondition & 0x01) == 0x01) {
1485                // ONS required when registered to HPLMN or PLMN in EF_SPDI
1486                rule |= SPN_RULE_SHOW_PLMN;
1487            }
1488        } else {
1489            rule = SPN_RULE_SHOW_PLMN;
1490            if ((mSpnDisplayCondition & 0x02) == 0x00) {
1491                // SPN required if not registered to HPLMN or PLMN in EF_SPDI
1492                rule |= SPN_RULE_SHOW_SPN;
1493            }
1494        }
1495        return rule;
1496    }
1497
1498    /**
1499     * Checks if plmn is HPLMN or on the spdiNetworks list.
1500     */
1501    private boolean isOnMatchingPlmn(String plmn) {
1502        if (plmn == null) return false;
1503
1504        if (plmn.equals(getOperatorNumeric())) {
1505            return true;
1506        }
1507
1508        if (mSpdiNetworks != null) {
1509            for (String spdiNet : mSpdiNetworks) {
1510                if (plmn.equals(spdiNet)) {
1511                    return true;
1512                }
1513            }
1514        }
1515        return false;
1516    }
1517
1518    /**
1519     * States of Get SPN Finite State Machine which only used by getSpnFsm()
1520     */
1521    private enum GetSpnFsmState {
1522        IDLE,               // No initialized
1523        INIT,               // Start FSM
1524        READ_SPN_3GPP,      // Load EF_SPN firstly
1525        READ_SPN_CPHS,      // Load EF_SPN_CPHS secondly
1526        READ_SPN_SHORT_CPHS // Load EF_SPN_SHORT_CPHS last
1527    }
1528
1529    /**
1530     * Finite State Machine to load Service Provider Name , which can be stored
1531     * in either EF_SPN (3GPP), EF_SPN_CPHS, or EF_SPN_SHORT_CPHS (CPHS4.2)
1532     *
1533     * After starting, FSM will search SPN EFs in order and stop after finding
1534     * the first valid SPN
1535     *
1536     * If the FSM gets restart while waiting for one of
1537     * SPN EFs results (i.e. a SIM refresh occurs after issuing
1538     * read EF_CPHS_SPN), it will re-initialize only after
1539     * receiving and discarding the unfinished SPN EF result.
1540     *
1541     * @param start set true only for initialize loading
1542     * @param ar the AsyncResult from loadEFTransparent
1543     *        ar.exception holds exception in error
1544     *        ar.result is byte[] for data in success
1545     */
1546    private void getSpnFsm(boolean start, AsyncResult ar) {
1547        byte[] data;
1548
1549        if (start) {
1550            // Check previous state to see if there is outstanding
1551            // SPN read
1552            if(mSpnState == GetSpnFsmState.READ_SPN_3GPP ||
1553               mSpnState == GetSpnFsmState.READ_SPN_CPHS ||
1554               mSpnState == GetSpnFsmState.READ_SPN_SHORT_CPHS ||
1555               mSpnState == GetSpnFsmState.INIT) {
1556                // Set INIT then return so the INIT code
1557                // will run when the outstanding read done.
1558                mSpnState = GetSpnFsmState.INIT;
1559                return;
1560            } else {
1561                mSpnState = GetSpnFsmState.INIT;
1562            }
1563        }
1564
1565        switch(mSpnState){
1566            case INIT:
1567                mSpn = null;
1568
1569                mFh.loadEFTransparent(EF_SPN,
1570                        obtainMessage(EVENT_GET_SPN_DONE));
1571                mRecordsToLoad++;
1572
1573                mSpnState = GetSpnFsmState.READ_SPN_3GPP;
1574                break;
1575            case READ_SPN_3GPP:
1576                if (ar != null && ar.exception == null) {
1577                    data = (byte[]) ar.result;
1578                    mSpnDisplayCondition = 0xff & data[0];
1579                    mSpn = IccUtils.adnStringFieldToString(data, 1, data.length - 1);
1580
1581                    if (DBG) log("Load EF_SPN: " + mSpn
1582                            + " spnDisplayCondition: " + mSpnDisplayCondition);
1583                    SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn);
1584
1585                    mSpnState = GetSpnFsmState.IDLE;
1586                } else {
1587                    mFh.loadEFTransparent( EF_SPN_CPHS,
1588                            obtainMessage(EVENT_GET_SPN_DONE));
1589                    mRecordsToLoad++;
1590
1591                    mSpnState = GetSpnFsmState.READ_SPN_CPHS;
1592
1593                    // See TS 51.011 10.3.11.  Basically, default to
1594                    // show PLMN always, and SPN also if roaming.
1595                    mSpnDisplayCondition = -1;
1596                }
1597                break;
1598            case READ_SPN_CPHS:
1599                if (ar != null && ar.exception == null) {
1600                    data = (byte[]) ar.result;
1601                    mSpn = IccUtils.adnStringFieldToString(data, 0, data.length);
1602
1603                    if (DBG) log("Load EF_SPN_CPHS: " + mSpn);
1604                    SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn);
1605
1606                    mSpnState = GetSpnFsmState.IDLE;
1607                } else {
1608                    mFh.loadEFTransparent(
1609                            EF_SPN_SHORT_CPHS, obtainMessage(EVENT_GET_SPN_DONE));
1610                    mRecordsToLoad++;
1611
1612                    mSpnState = GetSpnFsmState.READ_SPN_SHORT_CPHS;
1613                }
1614                break;
1615            case READ_SPN_SHORT_CPHS:
1616                if (ar != null && ar.exception == null) {
1617                    data = (byte[]) ar.result;
1618                    mSpn = IccUtils.adnStringFieldToString(data, 0, data.length);
1619
1620                    if (DBG) log("Load EF_SPN_SHORT_CPHS: " + mSpn);
1621                    SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn);
1622                }else {
1623                    if (DBG) log("No SPN loaded in either CHPS or 3GPP");
1624                }
1625
1626                mSpnState = GetSpnFsmState.IDLE;
1627                break;
1628            default:
1629                mSpnState = GetSpnFsmState.IDLE;
1630        }
1631    }
1632
1633    /**
1634     * Parse TS 51.011 EF[SPDI] record
1635     * This record contains the list of numeric network IDs that
1636     * are treated specially when determining SPN display
1637     */
1638    private void
1639    parseEfSpdi(byte[] data) {
1640        SimTlv tlv = new SimTlv(data, 0, data.length);
1641
1642        byte[] plmnEntries = null;
1643
1644        for ( ; tlv.isValidObject() ; tlv.nextObject()) {
1645            // Skip SPDI tag, if existant
1646            if (tlv.getTag() == TAG_SPDI) {
1647              tlv = new SimTlv(tlv.getData(), 0, tlv.getData().length);
1648            }
1649            // There should only be one TAG_SPDI_PLMN_LIST
1650            if (tlv.getTag() == TAG_SPDI_PLMN_LIST) {
1651                plmnEntries = tlv.getData();
1652                break;
1653            }
1654        }
1655
1656        if (plmnEntries == null) {
1657            return;
1658        }
1659
1660        mSpdiNetworks = new ArrayList<String>(plmnEntries.length / 3);
1661
1662        for (int i = 0 ; i + 2 < plmnEntries.length ; i += 3) {
1663            String plmnCode;
1664            plmnCode = IccUtils.bcdToString(plmnEntries, i, 3);
1665
1666            // Valid operator codes are 5 or 6 digits
1667            if (plmnCode.length() >= 5) {
1668                log("EF_SPDI network: " + plmnCode);
1669                mSpdiNetworks.add(plmnCode);
1670            }
1671        }
1672    }
1673
1674    /**
1675     * check to see if Mailbox Number is allocated and activated in CPHS SST
1676     */
1677    private boolean isCphsMailboxEnabled() {
1678        if (mCphsInfo == null)  return false;
1679        return ((mCphsInfo[1] & CPHS_SST_MBN_MASK) == CPHS_SST_MBN_ENABLED );
1680    }
1681
1682    @Override
1683    protected void log(String s) {
1684        Rlog.d(LOG_TAG, "[SIMRecords] " + s);
1685    }
1686
1687    @Override
1688    protected void loge(String s) {
1689        Rlog.e(LOG_TAG, "[SIMRecords] " + s);
1690    }
1691
1692    protected void logw(String s, Throwable tr) {
1693        Rlog.w(LOG_TAG, "[SIMRecords] " + s, tr);
1694    }
1695
1696    protected void logv(String s) {
1697        Rlog.v(LOG_TAG, "[SIMRecords] " + s);
1698    }
1699
1700    /**
1701     * Return true if "Restriction of menu options for manual PLMN selection"
1702     * bit is set or EF_CSP data is unavailable, return false otherwise.
1703     */
1704    @Override
1705    public boolean isCspPlmnEnabled() {
1706        return mCspPlmnEnabled;
1707    }
1708
1709    /**
1710     * Parse EF_CSP data and check if
1711     * "Restriction of menu options for manual PLMN selection" is
1712     * Enabled/Disabled
1713     *
1714     * @param data EF_CSP hex data.
1715     */
1716    private void handleEfCspData(byte[] data) {
1717        // As per spec CPHS4_2.WW6, CPHS B.4.7.1, EF_CSP contains CPHS defined
1718        // 18 bytes (i.e 9 service groups info) and additional data specific to
1719        // operator. The valueAddedServicesGroup is not part of standard
1720        // services. This is operator specific and can be programmed any where.
1721        // Normally this is programmed as 10th service after the standard
1722        // services.
1723        int usedCspGroups = data.length / 2;
1724        // This is the "Service Group Number" of "Value Added Services Group".
1725        byte valueAddedServicesGroup = (byte)0xC0;
1726
1727        mCspPlmnEnabled = true;
1728        for (int i = 0; i < usedCspGroups; i++) {
1729             if (data[2 * i] == valueAddedServicesGroup) {
1730                 log("[CSP] found ValueAddedServicesGroup, value " + data[(2 * i) + 1]);
1731                 if ((data[(2 * i) + 1] & 0x80) == 0x80) {
1732                     // Bit 8 is for
1733                     // "Restriction of menu options for manual PLMN selection".
1734                     // Operator Selection menu should be enabled.
1735                     mCspPlmnEnabled = true;
1736                 } else {
1737                     mCspPlmnEnabled = false;
1738                     // Operator Selection menu should be disabled.
1739                     // Operator Selection Mode should be set to Automatic.
1740                     log("[CSP] Set Automatic Network Selection");
1741                     mNetworkSelectionModeAutomaticRegistrants.notifyRegistrants();
1742                 }
1743                 return;
1744             }
1745        }
1746
1747        log("[CSP] Value Added Service Group (0xC0), not found!");
1748    }
1749
1750    @Override
1751    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1752        pw.println("SIMRecords: " + this);
1753        pw.println(" extends:");
1754        super.dump(fd, pw, args);
1755        pw.println(" mVmConfig=" + mVmConfig);
1756        pw.println(" mSpnOverride=" + mSpnOverride);
1757        pw.println(" mCallForwardingEnabled=" + mCallForwardingEnabled);
1758        pw.println(" mSpnState=" + mSpnState);
1759        pw.println(" mCphsInfo=" + mCphsInfo);
1760        pw.println(" mCspPlmnEnabled=" + mCspPlmnEnabled);
1761        pw.println(" mEfMWIS[]=" + Arrays.toString(mEfMWIS));
1762        pw.println(" mEfCPHS_MWI[]=" + Arrays.toString(mEfCPHS_MWI));
1763        pw.println(" mEfCff[]=" + Arrays.toString(mEfCff));
1764        pw.println(" mEfCfis[]=" + Arrays.toString(mEfCfis));
1765        pw.println(" mSpnDisplayCondition=" + mSpnDisplayCondition);
1766        pw.println(" mSpdiNetworks[]=" + mSpdiNetworks);
1767        pw.println(" mPnnHomeName=" + mPnnHomeName);
1768        pw.println(" mUsimServiceTable=" + mUsimServiceTable);
1769        pw.println(" mGid1=" + mGid1);
1770        pw.flush();
1771    }
1772}
1773