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