SIMRecords.java revision 2fe945394877c98706b4d56bcdc3dcde1eaccce8
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,
629                            mImsi.substring(0, 3 + mMncLength), false);
630                }
631                mImsiReadyRegistrants.notifyRegistrants();
632            break;
633
634            case EVENT_GET_MBI_DONE:
635                boolean isValidMbdn;
636                isRecordLoadResponse = true;
637
638                ar = (AsyncResult)msg.obj;
639                data = (byte[]) ar.result;
640
641                isValidMbdn = false;
642                if (ar.exception == null) {
643                    // Refer TS 51.011 Section 10.3.44 for content details
644                    log("EF_MBI: " + IccUtils.bytesToHexString(data));
645
646                    // Voice mail record number stored first
647                    mMailboxIndex = data[0] & 0xff;
648
649                    // check if dailing numbe id valid
650                    if (mMailboxIndex != 0 && mMailboxIndex != 0xff) {
651                        log("Got valid mailbox number for MBDN");
652                        isValidMbdn = true;
653                    }
654                }
655
656                // one more record to load
657                mRecordsToLoad += 1;
658
659                if (isValidMbdn) {
660                    // Note: MBDN was not included in NUM_OF_SIM_RECORDS_LOADED
661                    new AdnRecordLoader(mFh).loadFromEF(EF_MBDN, EF_EXT6,
662                            mMailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE));
663                } else {
664                    // If this EF not present, try mailbox as in CPHS standard
665                    // CPHS (CPHS4_2.WW6) is a european standard.
666                    new AdnRecordLoader(mFh).loadFromEF(EF_MAILBOX_CPHS,
667                            EF_EXT1, 1,
668                            obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
669                }
670
671                break;
672            case EVENT_GET_CPHS_MAILBOX_DONE:
673            case EVENT_GET_MBDN_DONE:
674                //Resetting the voice mail number and voice mail tag to null
675                //as these should be updated from the data read from EF_MBDN.
676                //If they are not reset, incase of invalid data/exception these
677                //variables are retaining their previous values and are
678                //causing invalid voice mailbox info display to user.
679                mVoiceMailNum = null;
680                mVoiceMailTag = null;
681                isRecordLoadResponse = true;
682
683                ar = (AsyncResult)msg.obj;
684
685                if (ar.exception != null) {
686
687                    log("Invalid or missing EF"
688                        + ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? "[MAILBOX]" : "[MBDN]"));
689
690                    // Bug #645770 fall back to CPHS
691                    // FIXME should use SST to decide
692
693                    if (msg.what == EVENT_GET_MBDN_DONE) {
694                        //load CPHS on fail...
695                        // FIXME right now, only load line1's CPHS voice mail entry
696
697                        mRecordsToLoad += 1;
698                        new AdnRecordLoader(mFh).loadFromEF(
699                                EF_MAILBOX_CPHS, EF_EXT1, 1,
700                                obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
701                    }
702                    break;
703                }
704
705                adn = (AdnRecord)ar.result;
706
707                log("VM: " + adn +
708                        ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? " EF[MAILBOX]" : " EF[MBDN]"));
709
710                if (adn.isEmpty() && msg.what == EVENT_GET_MBDN_DONE) {
711                    // Bug #645770 fall back to CPHS
712                    // FIXME should use SST to decide
713                    // FIXME right now, only load line1's CPHS voice mail entry
714                    mRecordsToLoad += 1;
715                    new AdnRecordLoader(mFh).loadFromEF(
716                            EF_MAILBOX_CPHS, EF_EXT1, 1,
717                            obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
718
719                    break;
720                }
721
722                mVoiceMailNum = adn.getNumber();
723                mVoiceMailTag = adn.getAlphaTag();
724            break;
725
726            case EVENT_GET_MSISDN_DONE:
727                isRecordLoadResponse = true;
728
729                ar = (AsyncResult)msg.obj;
730
731                if (ar.exception != null) {
732                    log("Invalid or missing EF[MSISDN]");
733                    break;
734                }
735
736                adn = (AdnRecord)ar.result;
737
738                mMsisdn = adn.getNumber();
739                mMsisdnTag = adn.getAlphaTag();
740
741                log("MSISDN: " + /*mMsisdn*/ "xxxxxxx");
742            break;
743
744            case EVENT_SET_MSISDN_DONE:
745                isRecordLoadResponse = false;
746                ar = (AsyncResult)msg.obj;
747
748                if (ar.userObj != null) {
749                    AsyncResult.forMessage(((Message) ar.userObj)).exception
750                            = ar.exception;
751                    ((Message) ar.userObj).sendToTarget();
752                }
753                break;
754
755            case EVENT_GET_MWIS_DONE:
756                isRecordLoadResponse = true;
757
758                ar = (AsyncResult)msg.obj;
759                data = (byte[])ar.result;
760
761                if (ar.exception != null) {
762                    break;
763                }
764
765                log("EF_MWIS: " + IccUtils.bytesToHexString(data));
766
767                mEfMWIS = data;
768
769                if ((data[0] & 0xff) == 0xff) {
770                    log("Uninitialized record MWIS");
771                    break;
772                }
773
774                // Refer TS 51.011 Section 10.3.45 for the content description
775                boolean voiceMailWaiting = ((data[0] & 0x01) != 0);
776                mCountVoiceMessages = data[1] & 0xff;
777
778                if (voiceMailWaiting && mCountVoiceMessages == 0) {
779                    // Unknown count = -1
780                    mCountVoiceMessages = -1;
781                }
782
783                mRecordsEventsRegistrants.notifyResult(EVENT_MWI);
784            break;
785
786            case EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE:
787                isRecordLoadResponse = true;
788
789                ar = (AsyncResult)msg.obj;
790                data = (byte[])ar.result;
791
792                if (ar.exception != null) {
793                    break;
794                }
795
796                mEfCPHS_MWI = data;
797
798                // Use this data if the EF[MWIS] exists and
799                // has been loaded
800
801                if (mEfMWIS == null) {
802                    int indicator = data[0] & 0xf;
803
804                    // Refer CPHS4_2.WW6 B4.2.3
805                    if (indicator == 0xA) {
806                        // Unknown count = -1
807                        mCountVoiceMessages = -1;
808                    } else if (indicator == 0x5) {
809                        mCountVoiceMessages = 0;
810                    }
811
812                    mRecordsEventsRegistrants.notifyResult(EVENT_MWI);
813                }
814            break;
815
816            case EVENT_GET_ICCID_DONE:
817                isRecordLoadResponse = true;
818
819                ar = (AsyncResult)msg.obj;
820                data = (byte[])ar.result;
821
822                if (ar.exception != null) {
823                    break;
824                }
825
826                mIccId = IccUtils.bcdToString(data, 0, data.length);
827
828                log("iccid: " + mIccId);
829
830            break;
831
832
833            case EVENT_GET_AD_DONE:
834                try {
835                    isRecordLoadResponse = true;
836
837                    ar = (AsyncResult)msg.obj;
838                    data = (byte[])ar.result;
839
840                    if (ar.exception != null) {
841                        break;
842                    }
843
844                    log("EF_AD: " + IccUtils.bytesToHexString(data));
845
846                    if (data.length < 3) {
847                        log("Corrupt AD data on SIM");
848                        break;
849                    }
850
851                    if (data.length == 3) {
852                        log("MNC length not present in EF_AD");
853                        break;
854                    }
855
856                    mMncLength = data[3] & 0xf;
857
858                    if (mMncLength == 0xf) {
859                        mMncLength = UNKNOWN;
860                    }
861                } finally {
862                    if (((mMncLength == UNINITIALIZED) || (mMncLength == UNKNOWN) ||
863                            (mMncLength == 2)) && ((mImsi != null) && (mImsi.length() >= 6))) {
864                        String mccmncCode = mImsi.substring(0, 6);
865                        for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
866                            if (mccmnc.equals(mccmncCode)) {
867                                mMncLength = 3;
868                                break;
869                            }
870                        }
871                    }
872
873                    if (mMncLength == UNKNOWN || mMncLength == UNINITIALIZED) {
874                        if (mImsi != null) {
875                            try {
876                                int mcc = Integer.parseInt(mImsi.substring(0,3));
877
878                                mMncLength = MccTable.smallestDigitsMccForMnc(mcc);
879                            } catch (NumberFormatException e) {
880                                mMncLength = UNKNOWN;
881                                loge("Corrupt IMSI!");
882                            }
883                        } else {
884                            // Indicate we got this info, but it didn't contain the length.
885                            mMncLength = UNKNOWN;
886
887                            log("MNC length not present in EF_AD");
888                        }
889                    }
890                    if (mImsi != null && mMncLength != UNKNOWN) {
891                        // finally have both imsi and the length of the mnc and can parse
892                        // the imsi properly
893                        MccTable.updateMccMncConfiguration(mContext,
894                                mImsi.substring(0, 3 + mMncLength), false);
895                    }
896                }
897            break;
898
899            case EVENT_GET_SPN_DONE:
900                isRecordLoadResponse = true;
901                ar = (AsyncResult) msg.obj;
902                getSpnFsm(false, ar);
903            break;
904
905            case EVENT_GET_CFF_DONE:
906                isRecordLoadResponse = true;
907
908                ar = (AsyncResult) msg.obj;
909                data = (byte[]) ar.result;
910
911                if (ar.exception != null) {
912                    break;
913                }
914
915                log("EF_CFF_CPHS: " + IccUtils.bytesToHexString(data));
916                mEfCff = data;
917
918                // if EF_CFIS is valid, prefer it to EF_CFF_CPHS
919                if (!validEfCfis(mEfCfis)) {
920                    mCallForwardingEnabled =
921                        ((data[0] & CFF_LINE1_MASK) == CFF_UNCONDITIONAL_ACTIVE);
922
923                    mRecordsEventsRegistrants.notifyResult(EVENT_CFI);
924                } else {
925                    log("EVENT_GET_CFF_DONE: EF_CFIS is valid, ignoring EF_CFF_CPHS");
926                }
927                break;
928
929            case EVENT_GET_SPDI_DONE:
930                isRecordLoadResponse = true;
931
932                ar = (AsyncResult)msg.obj;
933                data = (byte[])ar.result;
934
935                if (ar.exception != null) {
936                    break;
937                }
938
939                parseEfSpdi(data);
940            break;
941
942            case EVENT_UPDATE_DONE:
943                ar = (AsyncResult)msg.obj;
944                if (ar.exception != null) {
945                    logw("update failed. ", ar.exception);
946                }
947            break;
948
949            case EVENT_GET_PNN_DONE:
950                isRecordLoadResponse = true;
951
952                ar = (AsyncResult)msg.obj;
953                data = (byte[])ar.result;
954
955                if (ar.exception != null) {
956                    break;
957                }
958
959                SimTlv tlv = new SimTlv(data, 0, data.length);
960
961                for ( ; tlv.isValidObject() ; tlv.nextObject()) {
962                    if (tlv.getTag() == TAG_FULL_NETWORK_NAME) {
963                        mPnnHomeName
964                            = IccUtils.networkNameToString(
965                                tlv.getData(), 0, tlv.getData().length);
966                        break;
967                    }
968                }
969            break;
970
971            case EVENT_GET_ALL_SMS_DONE:
972                isRecordLoadResponse = true;
973
974                ar = (AsyncResult)msg.obj;
975                if (ar.exception != null)
976                    break;
977
978                handleSmses((ArrayList<byte []>) ar.result);
979                break;
980
981            case EVENT_MARK_SMS_READ_DONE:
982                Rlog.i("ENF", "marked read: sms " + msg.arg1);
983                break;
984
985
986            case EVENT_SMS_ON_SIM:
987                isRecordLoadResponse = false;
988
989                ar = (AsyncResult)msg.obj;
990
991                int[] index = (int[])ar.result;
992
993                if (ar.exception != null || index.length != 1) {
994                    loge("Error on SMS_ON_SIM with exp "
995                            + ar.exception + " length " + index.length);
996                } else {
997                    log("READ EF_SMS RECORD index=" + index[0]);
998                    mFh.loadEFLinearFixed(EF_SMS,index[0],
999                            obtainMessage(EVENT_GET_SMS_DONE));
1000                }
1001                break;
1002
1003            case EVENT_GET_SMS_DONE:
1004                isRecordLoadResponse = false;
1005                ar = (AsyncResult)msg.obj;
1006                if (ar.exception == null) {
1007                    handleSms((byte[])ar.result);
1008                } else {
1009                    loge("Error on GET_SMS with exp " + ar.exception);
1010                }
1011                break;
1012            case EVENT_GET_SST_DONE:
1013                isRecordLoadResponse = true;
1014
1015                ar = (AsyncResult)msg.obj;
1016                data = (byte[])ar.result;
1017
1018                if (ar.exception != null) {
1019                    break;
1020                }
1021
1022                mUsimServiceTable = new UsimServiceTable(data);
1023                if (DBG) log("SST: " + mUsimServiceTable);
1024                break;
1025
1026            case EVENT_GET_INFO_CPHS_DONE:
1027                isRecordLoadResponse = true;
1028
1029                ar = (AsyncResult)msg.obj;
1030
1031                if (ar.exception != null) {
1032                    break;
1033                }
1034
1035                mCphsInfo = (byte[])ar.result;
1036
1037                if (DBG) log("iCPHS: " + IccUtils.bytesToHexString(mCphsInfo));
1038            break;
1039
1040            case EVENT_SET_MBDN_DONE:
1041                isRecordLoadResponse = false;
1042                ar = (AsyncResult)msg.obj;
1043
1044                if (ar.exception == null) {
1045                    mVoiceMailNum = mNewVoiceMailNum;
1046                    mVoiceMailTag = mNewVoiceMailTag;
1047                }
1048
1049                if (isCphsMailboxEnabled()) {
1050                    adn = new AdnRecord(mVoiceMailTag, mVoiceMailNum);
1051                    Message onCphsCompleted = (Message) ar.userObj;
1052
1053                    /* write to cphs mailbox whenever it is available but
1054                    * we only need notify caller once if both updating are
1055                    * successful.
1056                    *
1057                    * so if set_mbdn successful, notify caller here and set
1058                    * onCphsCompleted to null
1059                    */
1060                    if (ar.exception == null && ar.userObj != null) {
1061                        AsyncResult.forMessage(((Message) ar.userObj)).exception
1062                                = null;
1063                        ((Message) ar.userObj).sendToTarget();
1064
1065                        if (DBG) log("Callback with MBDN successful.");
1066
1067                        onCphsCompleted = null;
1068                    }
1069
1070                    new AdnRecordLoader(mFh).
1071                            updateEF(adn, EF_MAILBOX_CPHS, EF_EXT1, 1, null,
1072                            obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE,
1073                                    onCphsCompleted));
1074                } else {
1075                    if (ar.userObj != null) {
1076                        AsyncResult.forMessage(((Message) ar.userObj)).exception
1077                                = ar.exception;
1078                        ((Message) ar.userObj).sendToTarget();
1079                    }
1080                }
1081                break;
1082            case EVENT_SET_CPHS_MAILBOX_DONE:
1083                isRecordLoadResponse = false;
1084                ar = (AsyncResult)msg.obj;
1085                if(ar.exception == null) {
1086                    mVoiceMailNum = mNewVoiceMailNum;
1087                    mVoiceMailTag = mNewVoiceMailTag;
1088                } else {
1089                    if (DBG) log("Set CPHS MailBox with exception: "
1090                            + ar.exception);
1091                }
1092                if (ar.userObj != null) {
1093                    if (DBG) log("Callback with CPHS MB successful.");
1094                    AsyncResult.forMessage(((Message) ar.userObj)).exception
1095                            = ar.exception;
1096                    ((Message) ar.userObj).sendToTarget();
1097                }
1098                break;
1099            case EVENT_SIM_REFRESH:
1100                isRecordLoadResponse = false;
1101                ar = (AsyncResult)msg.obj;
1102                if (DBG) log("Sim REFRESH with exception: " + ar.exception);
1103                if (ar.exception == null) {
1104                    handleSimRefresh((IccRefreshResponse)ar.result);
1105                }
1106                break;
1107            case EVENT_GET_CFIS_DONE:
1108                isRecordLoadResponse = true;
1109
1110                ar = (AsyncResult)msg.obj;
1111                data = (byte[])ar.result;
1112
1113                if (ar.exception != null) {
1114                    break;
1115                }
1116
1117                log("EF_CFIS: " + IccUtils.bytesToHexString(data));
1118
1119                if (validEfCfis(data)) {
1120                    mEfCfis = data;
1121
1122                    // Refer TS 51.011 Section 10.3.46 for the content description
1123                    mCallForwardingEnabled = ((data[1] & 0x01) != 0);
1124                    log("EF_CFIS: callForwardingEnabled=" + mCallForwardingEnabled);
1125
1126                    mRecordsEventsRegistrants.notifyResult(EVENT_CFI);
1127                } else {
1128                    log("EF_CFIS: invalid data=" + IccUtils.bytesToHexString(data));
1129                }
1130                break;
1131
1132            case EVENT_GET_CSP_CPHS_DONE:
1133                isRecordLoadResponse = true;
1134
1135                ar = (AsyncResult)msg.obj;
1136
1137                if (ar.exception != null) {
1138                    loge("Exception in fetching EF_CSP data " + ar.exception);
1139                    break;
1140                }
1141
1142                data = (byte[])ar.result;
1143
1144                log("EF_CSP: " + IccUtils.bytesToHexString(data));
1145                handleEfCspData(data);
1146                break;
1147
1148            case EVENT_GET_GID1_DONE:
1149                isRecordLoadResponse = true;
1150
1151                ar = (AsyncResult)msg.obj;
1152                data =(byte[])ar.result;
1153
1154                if (ar.exception != null) {
1155                    loge("Exception in get GID1 " + ar.exception);
1156                    mGid1 = null;
1157                    break;
1158                }
1159                mGid1 = IccUtils.bytesToHexString(data);
1160                log("GID1: " + mGid1);
1161
1162                break;
1163
1164            default:
1165                super.handleMessage(msg);   // IccRecords handles generic record load responses
1166
1167        }}catch (RuntimeException exc) {
1168            // I don't want these exceptions to be fatal
1169            logw("Exception parsing SIM record", exc);
1170        } finally {
1171            // Count up record load responses even if they are fails
1172            if (isRecordLoadResponse) {
1173                onRecordLoaded();
1174            }
1175        }
1176    }
1177
1178    private void handleFileUpdate(int efid) {
1179        switch(efid) {
1180            case EF_MBDN:
1181                mRecordsToLoad++;
1182                new AdnRecordLoader(mFh).loadFromEF(EF_MBDN, EF_EXT6,
1183                        mMailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE));
1184                break;
1185            case EF_MAILBOX_CPHS:
1186                mRecordsToLoad++;
1187                new AdnRecordLoader(mFh).loadFromEF(EF_MAILBOX_CPHS, EF_EXT1,
1188                        1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
1189                break;
1190            case EF_CSP_CPHS:
1191                mRecordsToLoad++;
1192                log("[CSP] SIM Refresh for EF_CSP_CPHS");
1193                mFh.loadEFTransparent(EF_CSP_CPHS,
1194                        obtainMessage(EVENT_GET_CSP_CPHS_DONE));
1195                break;
1196            case EF_FDN:
1197                if (DBG) log("SIM Refresh called for EF_FDN");
1198                mParentApp.queryFdn();
1199                break;
1200            default:
1201                // For now, fetch all records if this is not a
1202                // voicemail number.
1203                // TODO: Handle other cases, instead of fetching all.
1204                mAdnCache.reset();
1205                fetchSimRecords();
1206                break;
1207        }
1208    }
1209
1210    private void handleSimRefresh(IccRefreshResponse refreshResponse){
1211        if (refreshResponse == null) {
1212            if (DBG) log("handleSimRefresh received without input");
1213            return;
1214        }
1215
1216        if (refreshResponse.aid != null &&
1217                !refreshResponse.aid.equals(mParentApp.getAid())) {
1218            // This is for different app. Ignore.
1219            return;
1220        }
1221
1222        switch (refreshResponse.refreshResult) {
1223            case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE:
1224                if (DBG) log("handleSimRefresh with SIM_FILE_UPDATED");
1225                handleFileUpdate(refreshResponse.efId);
1226                break;
1227            case IccRefreshResponse.REFRESH_RESULT_INIT:
1228                if (DBG) log("handleSimRefresh with SIM_REFRESH_INIT");
1229                // need to reload all files (that we care about)
1230                onIccRefreshInit();
1231                break;
1232            case IccRefreshResponse.REFRESH_RESULT_RESET:
1233                if (DBG) log("handleSimRefresh with SIM_REFRESH_RESET");
1234                mCi.setRadioPower(false, null);
1235                /* Note: no need to call setRadioPower(true).  Assuming the desired
1236                * radio power state is still ON (as tracked by ServiceStateTracker),
1237                * ServiceStateTracker will call setRadioPower when it receives the
1238                * RADIO_STATE_CHANGED notification for the power off.  And if the
1239                * desired power state has changed in the interim, we don't want to
1240                * override it with an unconditional power on.
1241                */
1242                mAdnCache.reset();
1243                break;
1244            default:
1245                // unknown refresh operation
1246                if (DBG) log("handleSimRefresh with unknown operation");
1247                break;
1248        }
1249    }
1250
1251    /**
1252     * Dispatch 3GPP format message to registrant ({@code GSMPhone} or {@code CDMALTEPhone})
1253     * to pass to the 3GPP SMS dispatcher for delivery.
1254     */
1255    private int dispatchGsmMessage(SmsMessage message) {
1256        mNewSmsRegistrants.notifyResult(message);
1257        return 0;
1258    }
1259
1260    private void handleSms(byte[] ba) {
1261        if (ba[0] != 0)
1262            Rlog.d("ENF", "status : " + ba[0]);
1263
1264        // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1265        // 3 == "received by MS from network; message to be read"
1266        if (ba[0] == 3) {
1267            int n = ba.length;
1268
1269            // Note: Data may include trailing FF's.  That's OK; message
1270            // should still parse correctly.
1271            byte[] pdu = new byte[n - 1];
1272            System.arraycopy(ba, 1, pdu, 0, n - 1);
1273            SmsMessage message = SmsMessage.createFromPdu(pdu, SmsConstants.FORMAT_3GPP);
1274
1275            dispatchGsmMessage(message);
1276        }
1277    }
1278
1279
1280    private void handleSmses(ArrayList<byte[]> messages) {
1281        int count = messages.size();
1282
1283        for (int i = 0; i < count; i++) {
1284            byte[] ba = messages.get(i);
1285
1286            if (ba[0] != 0)
1287                Rlog.i("ENF", "status " + i + ": " + ba[0]);
1288
1289            // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1290            // 3 == "received by MS from network; message to be read"
1291
1292            if (ba[0] == 3) {
1293                int n = ba.length;
1294
1295                // Note: Data may include trailing FF's.  That's OK; message
1296                // should still parse correctly.
1297                byte[] pdu = new byte[n - 1];
1298                System.arraycopy(ba, 1, pdu, 0, n - 1);
1299                SmsMessage message = SmsMessage.createFromPdu(pdu, SmsConstants.FORMAT_3GPP);
1300
1301                dispatchGsmMessage(message);
1302
1303                // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1304                // 1 == "received by MS from network; message read"
1305
1306                ba[0] = 1;
1307
1308                if (false) { // FIXME: writing seems to crash RdoServD
1309                    mFh.updateEFLinearFixed(EF_SMS,
1310                            i, ba, null, obtainMessage(EVENT_MARK_SMS_READ_DONE, i));
1311                }
1312            }
1313        }
1314    }
1315
1316    @Override
1317    protected void onRecordLoaded() {
1318        // One record loaded successfully or failed, In either case
1319        // we need to update the recordsToLoad count
1320        mRecordsToLoad -= 1;
1321        if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested);
1322
1323        if (mRecordsToLoad == 0 && mRecordsRequested == true) {
1324            onAllRecordsLoaded();
1325        } else if (mRecordsToLoad < 0) {
1326            loge("recordsToLoad <0, programmer error suspected");
1327            mRecordsToLoad = 0;
1328        }
1329    }
1330
1331    @Override
1332    protected void onAllRecordsLoaded() {
1333        if (DBG) log("record load complete");
1334
1335        // Some fields require more than one SIM record to set
1336
1337        String operator = getOperatorNumeric();
1338        if (!TextUtils.isEmpty(operator)) {
1339            log("onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" +
1340                    operator + "'");
1341            SystemProperties.set(PROPERTY_ICC_OPERATOR_NUMERIC, operator);
1342        } else {
1343            log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping");
1344        }
1345
1346        if (!TextUtils.isEmpty(mImsi)) {
1347            log("onAllRecordsLoaded set mcc imsi=" + mImsi);
1348            SystemProperties.set(PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
1349                    MccTable.countryCodeForMcc(Integer.parseInt(mImsi.substring(0,3))));
1350        } else {
1351            log("onAllRecordsLoaded empty imsi skipping setting mcc");
1352        }
1353
1354        setVoiceMailByCountry(operator);
1355        setSpnFromConfig(operator);
1356
1357        mRecordsLoadedRegistrants.notifyRegistrants(
1358            new AsyncResult(null, null, null));
1359    }
1360
1361    //***** Private methods
1362
1363    private void setSpnFromConfig(String carrier) {
1364        if (mSpnOverride.containsCarrier(carrier)) {
1365            mSpn = mSpnOverride.getSpn(carrier);
1366        }
1367    }
1368
1369
1370    private void setVoiceMailByCountry (String spn) {
1371        if (mVmConfig.containsCarrier(spn)) {
1372            mIsVoiceMailFixed = true;
1373            mVoiceMailNum = mVmConfig.getVoiceMailNumber(spn);
1374            mVoiceMailTag = mVmConfig.getVoiceMailTag(spn);
1375        }
1376    }
1377
1378    @Override
1379    public void onReady() {
1380        fetchSimRecords();
1381    }
1382
1383    protected void fetchSimRecords() {
1384        mRecordsRequested = true;
1385
1386        if (DBG) log("fetchSimRecords " + mRecordsToLoad);
1387
1388        mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));
1389        mRecordsToLoad++;
1390
1391        mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
1392        mRecordsToLoad++;
1393
1394        // FIXME should examine EF[MSISDN]'s capability configuration
1395        // to determine which is the voice/data/fax line
1396        new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, EF_EXT1, 1,
1397                    obtainMessage(EVENT_GET_MSISDN_DONE));
1398        mRecordsToLoad++;
1399
1400        // Record number is subscriber profile
1401        mFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE));
1402        mRecordsToLoad++;
1403
1404        mFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE));
1405        mRecordsToLoad++;
1406
1407        // Record number is subscriber profile
1408        mFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE));
1409        mRecordsToLoad++;
1410
1411
1412        // Also load CPHS-style voice mail indicator, which stores
1413        // the same info as EF[MWIS]. If both exist, both are updated
1414        // but the EF[MWIS] data is preferred
1415        // Please note this must be loaded after EF[MWIS]
1416        mFh.loadEFTransparent(
1417                EF_VOICE_MAIL_INDICATOR_CPHS,
1418                obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE));
1419        mRecordsToLoad++;
1420
1421        // Same goes for Call Forward Status indicator: fetch both
1422        // EF[CFIS] and CPHS-EF, with EF[CFIS] preferred.
1423        mFh.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE));
1424        mRecordsToLoad++;
1425        mFh.loadEFTransparent(EF_CFF_CPHS, obtainMessage(EVENT_GET_CFF_DONE));
1426        mRecordsToLoad++;
1427
1428
1429        getSpnFsm(true, null);
1430
1431        mFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE));
1432        mRecordsToLoad++;
1433
1434        mFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE));
1435        mRecordsToLoad++;
1436
1437        mFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE));
1438        mRecordsToLoad++;
1439
1440        mFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE));
1441        mRecordsToLoad++;
1442
1443        mFh.loadEFTransparent(EF_CSP_CPHS,obtainMessage(EVENT_GET_CSP_CPHS_DONE));
1444        mRecordsToLoad++;
1445
1446        mFh.loadEFTransparent(EF_GID1, obtainMessage(EVENT_GET_GID1_DONE));
1447        mRecordsToLoad++;
1448
1449        // XXX should seek instead of examining them all
1450        if (false) { // XXX
1451            mFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE));
1452            mRecordsToLoad++;
1453        }
1454
1455        if (CRASH_RIL) {
1456            String sms = "0107912160130310f20404d0110041007030208054832b0120"
1457                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1458                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1459                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1460                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1461                         + "ffffffffffffffffffffffffffffff";
1462            byte[] ba = IccUtils.hexStringToBytes(sms);
1463
1464            mFh.updateEFLinearFixed(EF_SMS, 1, ba, null,
1465                            obtainMessage(EVENT_MARK_SMS_READ_DONE, 1));
1466        }
1467        if (DBG) log("fetchSimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested);
1468    }
1469
1470    /**
1471     * Returns the SpnDisplayRule based on settings on the SIM and the
1472     * specified plmn (currently-registered PLMN).  See TS 22.101 Annex A
1473     * and TS 51.011 10.3.11 for details.
1474     *
1475     * If the SPN is not found on the SIM or is empty, the rule is
1476     * always PLMN_ONLY.
1477     */
1478    @Override
1479    public int getDisplayRule(String plmn) {
1480        int rule;
1481        if (TextUtils.isEmpty(mSpn) || mSpnDisplayCondition == -1) {
1482            // No EF_SPN content was found on the SIM, or not yet loaded.  Just show ONS.
1483            rule = SPN_RULE_SHOW_PLMN;
1484        } else if (isOnMatchingPlmn(plmn)) {
1485            rule = SPN_RULE_SHOW_SPN;
1486            if ((mSpnDisplayCondition & 0x01) == 0x01) {
1487                // ONS required when registered to HPLMN or PLMN in EF_SPDI
1488                rule |= SPN_RULE_SHOW_PLMN;
1489            }
1490        } else {
1491            rule = SPN_RULE_SHOW_PLMN;
1492            if ((mSpnDisplayCondition & 0x02) == 0x00) {
1493                // SPN required if not registered to HPLMN or PLMN in EF_SPDI
1494                rule |= SPN_RULE_SHOW_SPN;
1495            }
1496        }
1497        return rule;
1498    }
1499
1500    /**
1501     * Checks if plmn is HPLMN or on the spdiNetworks list.
1502     */
1503    private boolean isOnMatchingPlmn(String plmn) {
1504        if (plmn == null) return false;
1505
1506        if (plmn.equals(getOperatorNumeric())) {
1507            return true;
1508        }
1509
1510        if (mSpdiNetworks != null) {
1511            for (String spdiNet : mSpdiNetworks) {
1512                if (plmn.equals(spdiNet)) {
1513                    return true;
1514                }
1515            }
1516        }
1517        return false;
1518    }
1519
1520    /**
1521     * States of Get SPN Finite State Machine which only used by getSpnFsm()
1522     */
1523    private enum GetSpnFsmState {
1524        IDLE,               // No initialized
1525        INIT,               // Start FSM
1526        READ_SPN_3GPP,      // Load EF_SPN firstly
1527        READ_SPN_CPHS,      // Load EF_SPN_CPHS secondly
1528        READ_SPN_SHORT_CPHS // Load EF_SPN_SHORT_CPHS last
1529    }
1530
1531    /**
1532     * Finite State Machine to load Service Provider Name , which can be stored
1533     * in either EF_SPN (3GPP), EF_SPN_CPHS, or EF_SPN_SHORT_CPHS (CPHS4.2)
1534     *
1535     * After starting, FSM will search SPN EFs in order and stop after finding
1536     * the first valid SPN
1537     *
1538     * If the FSM gets restart while waiting for one of
1539     * SPN EFs results (i.e. a SIM refresh occurs after issuing
1540     * read EF_CPHS_SPN), it will re-initialize only after
1541     * receiving and discarding the unfinished SPN EF result.
1542     *
1543     * @param start set true only for initialize loading
1544     * @param ar the AsyncResult from loadEFTransparent
1545     *        ar.exception holds exception in error
1546     *        ar.result is byte[] for data in success
1547     */
1548    private void getSpnFsm(boolean start, AsyncResult ar) {
1549        byte[] data;
1550
1551        if (start) {
1552            // Check previous state to see if there is outstanding
1553            // SPN read
1554            if(mSpnState == GetSpnFsmState.READ_SPN_3GPP ||
1555               mSpnState == GetSpnFsmState.READ_SPN_CPHS ||
1556               mSpnState == GetSpnFsmState.READ_SPN_SHORT_CPHS ||
1557               mSpnState == GetSpnFsmState.INIT) {
1558                // Set INIT then return so the INIT code
1559                // will run when the outstanding read done.
1560                mSpnState = GetSpnFsmState.INIT;
1561                return;
1562            } else {
1563                mSpnState = GetSpnFsmState.INIT;
1564            }
1565        }
1566
1567        switch(mSpnState){
1568            case INIT:
1569                mSpn = null;
1570
1571                mFh.loadEFTransparent(EF_SPN,
1572                        obtainMessage(EVENT_GET_SPN_DONE));
1573                mRecordsToLoad++;
1574
1575                mSpnState = GetSpnFsmState.READ_SPN_3GPP;
1576                break;
1577            case READ_SPN_3GPP:
1578                if (ar != null && ar.exception == null) {
1579                    data = (byte[]) ar.result;
1580                    mSpnDisplayCondition = 0xff & data[0];
1581                    mSpn = IccUtils.adnStringFieldToString(data, 1, data.length - 1);
1582
1583                    if (DBG) log("Load EF_SPN: " + mSpn
1584                            + " spnDisplayCondition: " + mSpnDisplayCondition);
1585                    SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn);
1586
1587                    mSpnState = GetSpnFsmState.IDLE;
1588                } else {
1589                    mFh.loadEFTransparent( EF_SPN_CPHS,
1590                            obtainMessage(EVENT_GET_SPN_DONE));
1591                    mRecordsToLoad++;
1592
1593                    mSpnState = GetSpnFsmState.READ_SPN_CPHS;
1594
1595                    // See TS 51.011 10.3.11.  Basically, default to
1596                    // show PLMN always, and SPN also if roaming.
1597                    mSpnDisplayCondition = -1;
1598                }
1599                break;
1600            case READ_SPN_CPHS:
1601                if (ar != null && ar.exception == null) {
1602                    data = (byte[]) ar.result;
1603                    mSpn = IccUtils.adnStringFieldToString(data, 0, data.length);
1604
1605                    if (DBG) log("Load EF_SPN_CPHS: " + mSpn);
1606                    SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn);
1607
1608                    mSpnState = GetSpnFsmState.IDLE;
1609                } else {
1610                    mFh.loadEFTransparent(
1611                            EF_SPN_SHORT_CPHS, obtainMessage(EVENT_GET_SPN_DONE));
1612                    mRecordsToLoad++;
1613
1614                    mSpnState = GetSpnFsmState.READ_SPN_SHORT_CPHS;
1615                }
1616                break;
1617            case READ_SPN_SHORT_CPHS:
1618                if (ar != null && ar.exception == null) {
1619                    data = (byte[]) ar.result;
1620                    mSpn = IccUtils.adnStringFieldToString(data, 0, data.length);
1621
1622                    if (DBG) log("Load EF_SPN_SHORT_CPHS: " + mSpn);
1623                    SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn);
1624                }else {
1625                    if (DBG) log("No SPN loaded in either CHPS or 3GPP");
1626                }
1627
1628                mSpnState = GetSpnFsmState.IDLE;
1629                break;
1630            default:
1631                mSpnState = GetSpnFsmState.IDLE;
1632        }
1633    }
1634
1635    /**
1636     * Parse TS 51.011 EF[SPDI] record
1637     * This record contains the list of numeric network IDs that
1638     * are treated specially when determining SPN display
1639     */
1640    private void
1641    parseEfSpdi(byte[] data) {
1642        SimTlv tlv = new SimTlv(data, 0, data.length);
1643
1644        byte[] plmnEntries = null;
1645
1646        for ( ; tlv.isValidObject() ; tlv.nextObject()) {
1647            // Skip SPDI tag, if existant
1648            if (tlv.getTag() == TAG_SPDI) {
1649              tlv = new SimTlv(tlv.getData(), 0, tlv.getData().length);
1650            }
1651            // There should only be one TAG_SPDI_PLMN_LIST
1652            if (tlv.getTag() == TAG_SPDI_PLMN_LIST) {
1653                plmnEntries = tlv.getData();
1654                break;
1655            }
1656        }
1657
1658        if (plmnEntries == null) {
1659            return;
1660        }
1661
1662        mSpdiNetworks = new ArrayList<String>(plmnEntries.length / 3);
1663
1664        for (int i = 0 ; i + 2 < plmnEntries.length ; i += 3) {
1665            String plmnCode;
1666            plmnCode = IccUtils.bcdToString(plmnEntries, i, 3);
1667
1668            // Valid operator codes are 5 or 6 digits
1669            if (plmnCode.length() >= 5) {
1670                log("EF_SPDI network: " + plmnCode);
1671                mSpdiNetworks.add(plmnCode);
1672            }
1673        }
1674    }
1675
1676    /**
1677     * check to see if Mailbox Number is allocated and activated in CPHS SST
1678     */
1679    private boolean isCphsMailboxEnabled() {
1680        if (mCphsInfo == null)  return false;
1681        return ((mCphsInfo[1] & CPHS_SST_MBN_MASK) == CPHS_SST_MBN_ENABLED );
1682    }
1683
1684    @Override
1685    protected void log(String s) {
1686        Rlog.d(LOG_TAG, "[SIMRecords] " + s);
1687    }
1688
1689    @Override
1690    protected void loge(String s) {
1691        Rlog.e(LOG_TAG, "[SIMRecords] " + s);
1692    }
1693
1694    protected void logw(String s, Throwable tr) {
1695        Rlog.w(LOG_TAG, "[SIMRecords] " + s, tr);
1696    }
1697
1698    protected void logv(String s) {
1699        Rlog.v(LOG_TAG, "[SIMRecords] " + s);
1700    }
1701
1702    /**
1703     * Return true if "Restriction of menu options for manual PLMN selection"
1704     * bit is set or EF_CSP data is unavailable, return false otherwise.
1705     */
1706    @Override
1707    public boolean isCspPlmnEnabled() {
1708        return mCspPlmnEnabled;
1709    }
1710
1711    /**
1712     * Parse EF_CSP data and check if
1713     * "Restriction of menu options for manual PLMN selection" is
1714     * Enabled/Disabled
1715     *
1716     * @param data EF_CSP hex data.
1717     */
1718    private void handleEfCspData(byte[] data) {
1719        // As per spec CPHS4_2.WW6, CPHS B.4.7.1, EF_CSP contains CPHS defined
1720        // 18 bytes (i.e 9 service groups info) and additional data specific to
1721        // operator. The valueAddedServicesGroup is not part of standard
1722        // services. This is operator specific and can be programmed any where.
1723        // Normally this is programmed as 10th service after the standard
1724        // services.
1725        int usedCspGroups = data.length / 2;
1726        // This is the "Service Group Number" of "Value Added Services Group".
1727        byte valueAddedServicesGroup = (byte)0xC0;
1728
1729        mCspPlmnEnabled = true;
1730        for (int i = 0; i < usedCspGroups; i++) {
1731             if (data[2 * i] == valueAddedServicesGroup) {
1732                 log("[CSP] found ValueAddedServicesGroup, value " + data[(2 * i) + 1]);
1733                 if ((data[(2 * i) + 1] & 0x80) == 0x80) {
1734                     // Bit 8 is for
1735                     // "Restriction of menu options for manual PLMN selection".
1736                     // Operator Selection menu should be enabled.
1737                     mCspPlmnEnabled = true;
1738                 } else {
1739                     mCspPlmnEnabled = false;
1740                     // Operator Selection menu should be disabled.
1741                     // Operator Selection Mode should be set to Automatic.
1742                     log("[CSP] Set Automatic Network Selection");
1743                     mNetworkSelectionModeAutomaticRegistrants.notifyRegistrants();
1744                 }
1745                 return;
1746             }
1747        }
1748
1749        log("[CSP] Value Added Service Group (0xC0), not found!");
1750    }
1751
1752    @Override
1753    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1754        pw.println("SIMRecords: " + this);
1755        pw.println(" extends:");
1756        super.dump(fd, pw, args);
1757        pw.println(" mVmConfig=" + mVmConfig);
1758        pw.println(" mSpnOverride=" + mSpnOverride);
1759        pw.println(" mCallForwardingEnabled=" + mCallForwardingEnabled);
1760        pw.println(" mSpnState=" + mSpnState);
1761        pw.println(" mCphsInfo=" + mCphsInfo);
1762        pw.println(" mCspPlmnEnabled=" + mCspPlmnEnabled);
1763        pw.println(" mEfMWIS[]=" + Arrays.toString(mEfMWIS));
1764        pw.println(" mEfCPHS_MWI[]=" + Arrays.toString(mEfCPHS_MWI));
1765        pw.println(" mEfCff[]=" + Arrays.toString(mEfCff));
1766        pw.println(" mEfCfis[]=" + Arrays.toString(mEfCfis));
1767        pw.println(" mSpnDisplayCondition=" + mSpnDisplayCondition);
1768        pw.println(" mSpdiNetworks[]=" + mSpdiNetworks);
1769        pw.println(" mPnnHomeName=" + mPnnHomeName);
1770        pw.println(" mUsimServiceTable=" + mUsimServiceTable);
1771        pw.println(" mGid1=" + mGid1);
1772        pw.flush();
1773    }
1774}
1775