SIMRecords.java revision b9adaa1c4a12df8c8bd44b4803d452662b53e0d8
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;
22
23import android.content.Context;
24import android.os.AsyncResult;
25import android.os.Message;
26import android.os.SystemProperties;
27import android.telephony.TelephonyManager;
28import android.telephony.PhoneNumberUtils;
29import android.telephony.SmsMessage;
30import android.text.TextUtils;
31import android.telephony.Rlog;
32
33import com.android.internal.telephony.CommandsInterface;
34import com.android.internal.telephony.MccTable;
35import com.android.internal.telephony.SmsConstants;
36import com.android.internal.telephony.SubscriptionController;
37import com.android.internal.telephony.gsm.SimTlv;
38
39import java.io.FileDescriptor;
40import java.io.PrintWriter;
41import java.util.ArrayList;
42import java.util.Arrays;
43
44/**
45 * {@hide}
46 */
47public class SIMRecords extends IccRecords {
48    protected static final String LOG_TAG = "SIMRecords";
49
50    private static final boolean CRASH_RIL = false;
51
52    // ***** Instance Variables
53
54    VoiceMailConstants mVmConfig;
55
56
57    SpnOverride mSpnOverride;
58
59    // ***** Cached SIM State; cleared on channel close
60
61    private boolean mCallForwardingEnabled;
62
63
64    /**
65     * States only used by getSpnFsm FSM
66     */
67    private GetSpnFsmState mSpnState;
68
69    /** CPHS service information (See CPHS 4.2 B.3.1.1)
70     *  It will be set in onSimReady if reading GET_CPHS_INFO successfully
71     *  mCphsInfo[0] is CPHS Phase
72     *  mCphsInfo[1] and mCphsInfo[2] is CPHS Service Table
73     */
74    private byte[] mCphsInfo = null;
75    boolean mCspPlmnEnabled = true;
76
77    byte[] mEfMWIS = null;
78    byte[] mEfCPHS_MWI =null;
79    byte[] mEfCff = null;
80    byte[] mEfCfis = null;
81
82
83    int mSpnDisplayCondition;
84    // Numeric network codes listed in TS 51.011 EF[SPDI]
85    ArrayList<String> mSpdiNetworks = null;
86
87    String mPnnHomeName = null;
88
89    UsimServiceTable mUsimServiceTable;
90
91    @Override
92    public String toString() {
93        return "SimRecords: " + super.toString()
94                + " mVmConfig" + mVmConfig
95                + " mSpnOverride=" + "mSpnOverride"
96                + " callForwardingEnabled=" + mCallForwardingEnabled
97                + " spnState=" + mSpnState
98                + " mCphsInfo=" + mCphsInfo
99                + " mCspPlmnEnabled=" + mCspPlmnEnabled
100                + " efMWIS=" + mEfMWIS
101                + " efCPHS_MWI=" + mEfCPHS_MWI
102                + " mEfCff=" + mEfCff
103                + " mEfCfis=" + mEfCfis
104                + " getOperatorNumeric=" + getOperatorNumeric();
105    }
106
107    // ***** Constants
108
109    // From TS 51.011 EF[SPDI] section
110    static final int TAG_SPDI = 0xA3;
111    static final int TAG_SPDI_PLMN_LIST = 0x80;
112
113    // Full Name IEI from TS 24.008
114    static final int TAG_FULL_NETWORK_NAME = 0x43;
115
116    // Short Name IEI from TS 24.008
117    static final int TAG_SHORT_NETWORK_NAME = 0x45;
118
119    // active CFF from CPHS 4.2 B.4.5
120    static final int CFF_UNCONDITIONAL_ACTIVE = 0x0a;
121    static final int CFF_UNCONDITIONAL_DEACTIVE = 0x05;
122    static final int CFF_LINE1_MASK = 0x0f;
123    static final int CFF_LINE1_RESET = 0xf0;
124
125    // CPHS Service Table (See CPHS 4.2 B.3.1)
126    private static final int CPHS_SST_MBN_MASK = 0x30;
127    private static final int CPHS_SST_MBN_ENABLED = 0x30;
128
129    // EF_CFIS related constants
130    // Spec reference TS 51.011 section 10.3.46.
131    private static final int CFIS_BCD_NUMBER_LENGTH_OFFSET = 2;
132    private static final int CFIS_TON_NPI_OFFSET = 3;
133    private static final int CFIS_ADN_CAPABILITY_ID_OFFSET = 14;
134    private static final int CFIS_ADN_EXTENSION_ID_OFFSET = 15;
135
136    // ***** Event Constants
137    private static final int EVENT_GET_IMSI_DONE = 3;
138    private static final int EVENT_GET_ICCID_DONE = 4;
139    private static final int EVENT_GET_MBI_DONE = 5;
140    private static final int EVENT_GET_MBDN_DONE = 6;
141    private static final int EVENT_GET_MWIS_DONE = 7;
142    private static final int EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE = 8;
143    protected static final int EVENT_GET_AD_DONE = 9; // Admin data on SIM
144    protected static final int EVENT_GET_MSISDN_DONE = 10;
145    private static final int EVENT_GET_CPHS_MAILBOX_DONE = 11;
146    private static final int EVENT_GET_SPN_DONE = 12;
147    private static final int EVENT_GET_SPDI_DONE = 13;
148    private static final int EVENT_UPDATE_DONE = 14;
149    private static final int EVENT_GET_PNN_DONE = 15;
150    protected static final int EVENT_GET_SST_DONE = 17;
151    private static final int EVENT_GET_ALL_SMS_DONE = 18;
152    private static final int EVENT_MARK_SMS_READ_DONE = 19;
153    private static final int EVENT_SET_MBDN_DONE = 20;
154    private static final int EVENT_SMS_ON_SIM = 21;
155    private static final int EVENT_GET_SMS_DONE = 22;
156    private static final int EVENT_GET_CFF_DONE = 24;
157    private static final int EVENT_SET_CPHS_MAILBOX_DONE = 25;
158    private static final int EVENT_GET_INFO_CPHS_DONE = 26;
159    // private static final int EVENT_SET_MSISDN_DONE = 30; Defined in IccRecords as 30
160    private static final int EVENT_SIM_REFRESH = 31;
161    private static final int EVENT_GET_CFIS_DONE = 32;
162    private static final int EVENT_GET_CSP_CPHS_DONE = 33;
163    private static final int EVENT_GET_GID1_DONE = 34;
164
165    // Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length.
166
167    private static final String[] MCCMNC_CODES_HAVING_3DIGITS_MNC = {
168        "302370", "302720", "310260",
169        "405025", "405026", "405027", "405028", "405029", "405030", "405031", "405032",
170        "405033", "405034", "405035", "405036", "405037", "405038", "405039", "405040",
171        "405041", "405042", "405043", "405044", "405045", "405046", "405047", "405750",
172        "405751", "405752", "405753", "405754", "405755", "405756", "405799", "405800",
173        "405801", "405802", "405803", "405804", "405805", "405806", "405807", "405808",
174        "405809", "405810", "405811", "405812", "405813", "405814", "405815", "405816",
175        "405817", "405818", "405819", "405820", "405821", "405822", "405823", "405824",
176        "405825", "405826", "405827", "405828", "405829", "405830", "405831", "405832",
177        "405833", "405834", "405835", "405836", "405837", "405838", "405839", "405840",
178        "405841", "405842", "405843", "405844", "405845", "405846", "405847", "405848",
179        "405849", "405850", "405851", "405852", "405853", "405875", "405876", "405877",
180        "405878", "405879", "405880", "405881", "405882", "405883", "405884", "405885",
181        "405886", "405908", "405909", "405910", "405911", "405912", "405913", "405914",
182        "405915", "405916", "405917", "405918", "405919", "405920", "405921", "405922",
183        "405923", "405924", "405925", "405926", "405927", "405928", "405929", "405930",
184        "405931", "405932", "502142", "502143", "502145", "502146", "502147", "502148"
185    };
186
187    // ***** Constructor
188
189    public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
190        super(app, c, ci);
191
192        mAdnCache = new AdnRecordCache(mFh);
193
194        mVmConfig = new VoiceMailConstants();
195        mSpnOverride = new SpnOverride();
196
197        mRecordsRequested = false;  // No load request is made till SIM ready
198
199        // recordsToLoad is set to 0 because no requests are made yet
200        mRecordsToLoad = 0;
201
202        mCi.setOnSmsOnSim(this, EVENT_SMS_ON_SIM, null);
203        mCi.registerForIccRefresh(this, EVENT_SIM_REFRESH, null);
204
205        // Start off by setting empty state
206        resetRecords();
207        mParentApp.registerForReady(this, EVENT_APP_READY, null);
208        if (DBG) log("SIMRecords X ctor this=" + this);
209    }
210
211    @Override
212    public void dispose() {
213        if (DBG) log("Disposing SIMRecords this=" + this);
214        //Unregister for all events
215        mCi.unregisterForIccRefresh(this);
216        mCi.unSetOnSmsOnSim(this);
217        mParentApp.unregisterForReady(this);
218        resetRecords();
219        super.dispose();
220    }
221
222    @Override
223    protected void finalize() {
224        if(DBG) log("finalized");
225    }
226
227    protected void resetRecords() {
228        mImsi = null;
229        mMsisdn = null;
230        mVoiceMailNum = null;
231        mCountVoiceMessages = 0;
232        mMncLength = UNINITIALIZED;
233        log("setting0 mMncLength" + mMncLength);
234        mIccId = null;
235        // -1 means no EF_SPN found; treat accordingly.
236        mSpnDisplayCondition = -1;
237        mEfMWIS = null;
238        mEfCPHS_MWI = null;
239        mSpdiNetworks = null;
240        mPnnHomeName = null;
241        mGid1 = null;
242
243        mAdnCache.reset();
244
245        log("SIMRecords: onRadioOffOrNotAvailable set 'gsm.sim.operator.numeric' to operator=null");
246        log("update icc_operator_numeric=" + null);
247        setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, null);
248        setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, null);
249        setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, null);
250
251        // recordsRequested is set to false indicating that the SIM
252        // read requests made so far are not valid. This is set to
253        // true only when fresh set of read requests are made.
254        mRecordsRequested = false;
255    }
256
257
258    //***** Public Methods
259
260    /**
261     * {@inheritDoc}
262     */
263    @Override
264    public String getIMSI() {
265        return mImsi;
266    }
267
268    @Override
269    public String getMsisdnNumber() {
270        return mMsisdn;
271    }
272
273    @Override
274    public String getGid1() {
275        return mGid1;
276    }
277
278    @Override
279    public UsimServiceTable getUsimServiceTable() {
280        return mUsimServiceTable;
281    }
282
283    /**
284     * Set subscriber number to SIM record
285     *
286     * The subscriber number is stored in EF_MSISDN (TS 51.011)
287     *
288     * When the operation is complete, onComplete will be sent to its handler
289     *
290     * @param alphaTag alpha-tagging of the dailing nubmer (up to 10 characters)
291     * @param number dailing nubmer (up to 20 digits)
292     *        if the number starts with '+', then set to international TOA
293     * @param onComplete
294     *        onComplete.obj will be an AsyncResult
295     *        ((AsyncResult)onComplete.obj).exception == null on success
296     *        ((AsyncResult)onComplete.obj).exception != null on fail
297     */
298    @Override
299    public void setMsisdnNumber(String alphaTag, String number,
300            Message onComplete) {
301
302        mMsisdn = number;
303        mMsisdnTag = alphaTag;
304
305        if(DBG) log("Set MSISDN: " + mMsisdnTag + " " + /*mMsisdn*/ "xxxxxxx");
306
307
308        AdnRecord adn = new AdnRecord(mMsisdnTag, mMsisdn);
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.userObj != null) {
758                    AsyncResult.forMessage(((Message) ar.userObj)).exception
759                            = ar.exception;
760                    ((Message) ar.userObj).sendToTarget();
761                }
762                break;
763
764            case EVENT_GET_MWIS_DONE:
765                isRecordLoadResponse = true;
766
767                ar = (AsyncResult)msg.obj;
768                data = (byte[])ar.result;
769
770                if (ar.exception != null) {
771                    break;
772                }
773
774                log("EF_MWIS: " + IccUtils.bytesToHexString(data));
775
776                mEfMWIS = data;
777
778                if ((data[0] & 0xff) == 0xff) {
779                    log("Uninitialized record MWIS");
780                    break;
781                }
782
783                // Refer TS 51.011 Section 10.3.45 for the content description
784                boolean voiceMailWaiting = ((data[0] & 0x01) != 0);
785                mCountVoiceMessages = data[1] & 0xff;
786
787                if (voiceMailWaiting && mCountVoiceMessages == 0) {
788                    // Unknown count = -1
789                    mCountVoiceMessages = -1;
790                }
791
792                mRecordsEventsRegistrants.notifyResult(EVENT_MWI);
793            break;
794
795            case EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE:
796                isRecordLoadResponse = true;
797
798                ar = (AsyncResult)msg.obj;
799                data = (byte[])ar.result;
800
801                if (ar.exception != null) {
802                    break;
803                }
804
805                mEfCPHS_MWI = data;
806
807                // Use this data if the EF[MWIS] exists and
808                // has been loaded
809
810                if (mEfMWIS == null) {
811                    int indicator = data[0] & 0xf;
812
813                    // Refer CPHS4_2.WW6 B4.2.3
814                    if (indicator == 0xA) {
815                        // Unknown count = -1
816                        mCountVoiceMessages = -1;
817                    } else if (indicator == 0x5) {
818                        mCountVoiceMessages = 0;
819                    }
820
821                    mRecordsEventsRegistrants.notifyResult(EVENT_MWI);
822                }
823            break;
824
825            case EVENT_GET_ICCID_DONE:
826                isRecordLoadResponse = true;
827
828                ar = (AsyncResult)msg.obj;
829                data = (byte[])ar.result;
830
831                if (ar.exception != null) {
832                    break;
833                }
834
835                mIccId = IccUtils.bcdToString(data, 0, data.length);
836
837                log("iccid: " + mIccId);
838
839            break;
840
841
842            case EVENT_GET_AD_DONE:
843                try {
844                    isRecordLoadResponse = true;
845
846                    ar = (AsyncResult)msg.obj;
847                    data = (byte[])ar.result;
848
849                    if (ar.exception != null) {
850                        break;
851                    }
852
853                    log("EF_AD: " + IccUtils.bytesToHexString(data));
854
855                    if (data.length < 3) {
856                        log("Corrupt AD data on SIM");
857                        break;
858                    }
859
860                    if (data.length == 3) {
861                        log("MNC length not present in EF_AD");
862                        break;
863                    }
864
865                    mMncLength = data[3] & 0xf;
866                    log("setting4 mMncLength=" + mMncLength);
867
868                    if (mMncLength == 0xf) {
869                        mMncLength = UNKNOWN;
870                        log("setting5 mMncLength=" + mMncLength);
871                    }
872                } finally {
873                    if (((mMncLength == UNINITIALIZED) || (mMncLength == UNKNOWN) ||
874                            (mMncLength == 2)) && ((mImsi != null) && (mImsi.length() >= 6))) {
875                        String mccmncCode = mImsi.substring(0, 6);
876                        log("mccmncCode=" + mccmncCode);
877                        for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
878                            if (mccmnc.equals(mccmncCode)) {
879                                mMncLength = 3;
880                                log("setting6 mMncLength=" + mMncLength);
881                                break;
882                            }
883                        }
884                    }
885
886                    if (mMncLength == UNKNOWN || mMncLength == UNINITIALIZED) {
887                        if (mImsi != null) {
888                            try {
889                                int mcc = Integer.parseInt(mImsi.substring(0,3));
890
891                                mMncLength = MccTable.smallestDigitsMccForMnc(mcc);
892                                log("setting7 mMncLength=" + mMncLength);
893                            } catch (NumberFormatException e) {
894                                mMncLength = UNKNOWN;
895                                loge("Corrupt IMSI! setting8 mMncLength=" + mMncLength);
896                            }
897                        } else {
898                            // Indicate we got this info, but it didn't contain the length.
899                            mMncLength = UNKNOWN;
900                            log("MNC length not present in EF_AD setting9 mMncLength=" + mMncLength);
901                        }
902                    }
903                    if (mImsi != null && mMncLength != UNKNOWN) {
904                        // finally have both imsi and the length of the mnc and can parse
905                        // the imsi properly
906                        log("update mccmnc=" + mImsi.substring(0, 3 + mMncLength));
907                        MccTable.updateMccMncConfiguration(mContext,
908                                mImsi.substring(0, 3 + mMncLength), false);
909                    }
910                }
911            break;
912
913            case EVENT_GET_SPN_DONE:
914                isRecordLoadResponse = true;
915                ar = (AsyncResult) msg.obj;
916                getSpnFsm(false, ar);
917            break;
918
919            case EVENT_GET_CFF_DONE:
920                isRecordLoadResponse = true;
921
922                ar = (AsyncResult) msg.obj;
923                data = (byte[]) ar.result;
924
925                if (ar.exception != null) {
926                    break;
927                }
928
929                log("EF_CFF_CPHS: " + IccUtils.bytesToHexString(data));
930                mEfCff = data;
931
932                // if EF_CFIS is valid, prefer it to EF_CFF_CPHS
933                if (!validEfCfis(mEfCfis)) {
934                    mCallForwardingEnabled =
935                        ((data[0] & CFF_LINE1_MASK) == CFF_UNCONDITIONAL_ACTIVE);
936
937                    mRecordsEventsRegistrants.notifyResult(EVENT_CFI);
938                } else {
939                    log("EVENT_GET_CFF_DONE: EF_CFIS is valid, ignoring EF_CFF_CPHS");
940                }
941                break;
942
943            case EVENT_GET_SPDI_DONE:
944                isRecordLoadResponse = true;
945
946                ar = (AsyncResult)msg.obj;
947                data = (byte[])ar.result;
948
949                if (ar.exception != null) {
950                    break;
951                }
952
953                parseEfSpdi(data);
954            break;
955
956            case EVENT_UPDATE_DONE:
957                ar = (AsyncResult)msg.obj;
958                if (ar.exception != null) {
959                    logw("update failed. ", ar.exception);
960                }
961            break;
962
963            case EVENT_GET_PNN_DONE:
964                isRecordLoadResponse = true;
965
966                ar = (AsyncResult)msg.obj;
967                data = (byte[])ar.result;
968
969                if (ar.exception != null) {
970                    break;
971                }
972
973                SimTlv tlv = new SimTlv(data, 0, data.length);
974
975                for ( ; tlv.isValidObject() ; tlv.nextObject()) {
976                    if (tlv.getTag() == TAG_FULL_NETWORK_NAME) {
977                        mPnnHomeName
978                            = IccUtils.networkNameToString(
979                                tlv.getData(), 0, tlv.getData().length);
980                        break;
981                    }
982                }
983            break;
984
985            case EVENT_GET_ALL_SMS_DONE:
986                isRecordLoadResponse = true;
987
988                ar = (AsyncResult)msg.obj;
989                if (ar.exception != null)
990                    break;
991
992                handleSmses((ArrayList<byte []>) ar.result);
993                break;
994
995            case EVENT_MARK_SMS_READ_DONE:
996                Rlog.i("ENF", "marked read: sms " + msg.arg1);
997                break;
998
999
1000            case EVENT_SMS_ON_SIM:
1001                isRecordLoadResponse = false;
1002
1003                ar = (AsyncResult)msg.obj;
1004
1005                int[] index = (int[])ar.result;
1006
1007                if (ar.exception != null || index.length != 1) {
1008                    loge("Error on SMS_ON_SIM with exp "
1009                            + ar.exception + " length " + index.length);
1010                } else {
1011                    log("READ EF_SMS RECORD index=" + index[0]);
1012                    mFh.loadEFLinearFixed(EF_SMS,index[0],
1013                            obtainMessage(EVENT_GET_SMS_DONE));
1014                }
1015                break;
1016
1017            case EVENT_GET_SMS_DONE:
1018                isRecordLoadResponse = false;
1019                ar = (AsyncResult)msg.obj;
1020                if (ar.exception == null) {
1021                    handleSms((byte[])ar.result);
1022                } else {
1023                    loge("Error on GET_SMS with exp " + ar.exception);
1024                }
1025                break;
1026            case EVENT_GET_SST_DONE:
1027                isRecordLoadResponse = true;
1028
1029                ar = (AsyncResult)msg.obj;
1030                data = (byte[])ar.result;
1031
1032                if (ar.exception != null) {
1033                    break;
1034                }
1035
1036                mUsimServiceTable = new UsimServiceTable(data);
1037                if (DBG) log("SST: " + mUsimServiceTable);
1038                break;
1039
1040            case EVENT_GET_INFO_CPHS_DONE:
1041                isRecordLoadResponse = true;
1042
1043                ar = (AsyncResult)msg.obj;
1044
1045                if (ar.exception != null) {
1046                    break;
1047                }
1048
1049                mCphsInfo = (byte[])ar.result;
1050
1051                if (DBG) log("iCPHS: " + IccUtils.bytesToHexString(mCphsInfo));
1052            break;
1053
1054            case EVENT_SET_MBDN_DONE:
1055                isRecordLoadResponse = false;
1056                ar = (AsyncResult)msg.obj;
1057
1058                if (ar.exception == null) {
1059                    mVoiceMailNum = mNewVoiceMailNum;
1060                    mVoiceMailTag = mNewVoiceMailTag;
1061                }
1062
1063                if (isCphsMailboxEnabled()) {
1064                    adn = new AdnRecord(mVoiceMailTag, mVoiceMailNum);
1065                    Message onCphsCompleted = (Message) ar.userObj;
1066
1067                    /* write to cphs mailbox whenever it is available but
1068                    * we only need notify caller once if both updating are
1069                    * successful.
1070                    *
1071                    * so if set_mbdn successful, notify caller here and set
1072                    * onCphsCompleted to null
1073                    */
1074                    if (ar.exception == null && ar.userObj != null) {
1075                        AsyncResult.forMessage(((Message) ar.userObj)).exception
1076                                = null;
1077                        ((Message) ar.userObj).sendToTarget();
1078
1079                        if (DBG) log("Callback with MBDN successful.");
1080
1081                        onCphsCompleted = null;
1082                    }
1083
1084                    new AdnRecordLoader(mFh).
1085                            updateEF(adn, EF_MAILBOX_CPHS, EF_EXT1, 1, null,
1086                            obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE,
1087                                    onCphsCompleted));
1088                } else {
1089                    if (ar.userObj != null) {
1090                        AsyncResult.forMessage(((Message) ar.userObj)).exception
1091                                = ar.exception;
1092                        ((Message) ar.userObj).sendToTarget();
1093                    }
1094                }
1095                break;
1096            case EVENT_SET_CPHS_MAILBOX_DONE:
1097                isRecordLoadResponse = false;
1098                ar = (AsyncResult)msg.obj;
1099                if(ar.exception == null) {
1100                    mVoiceMailNum = mNewVoiceMailNum;
1101                    mVoiceMailTag = mNewVoiceMailTag;
1102                } else {
1103                    if (DBG) log("Set CPHS MailBox with exception: "
1104                            + ar.exception);
1105                }
1106                if (ar.userObj != null) {
1107                    if (DBG) log("Callback with CPHS MB successful.");
1108                    AsyncResult.forMessage(((Message) ar.userObj)).exception
1109                            = ar.exception;
1110                    ((Message) ar.userObj).sendToTarget();
1111                }
1112                break;
1113            case EVENT_SIM_REFRESH:
1114                isRecordLoadResponse = false;
1115                ar = (AsyncResult)msg.obj;
1116                if (DBG) log("Sim REFRESH with exception: " + ar.exception);
1117                if (ar.exception == null) {
1118                    handleSimRefresh((IccRefreshResponse)ar.result);
1119                }
1120                break;
1121            case EVENT_GET_CFIS_DONE:
1122                isRecordLoadResponse = true;
1123
1124                ar = (AsyncResult)msg.obj;
1125                data = (byte[])ar.result;
1126
1127                if (ar.exception != null) {
1128                    break;
1129                }
1130
1131                log("EF_CFIS: " + IccUtils.bytesToHexString(data));
1132
1133                if (validEfCfis(data)) {
1134                    mEfCfis = data;
1135
1136                    // Refer TS 51.011 Section 10.3.46 for the content description
1137                    mCallForwardingEnabled = ((data[1] & 0x01) != 0);
1138                    log("EF_CFIS: callForwardingEnabled=" + mCallForwardingEnabled);
1139
1140                    mRecordsEventsRegistrants.notifyResult(EVENT_CFI);
1141                } else {
1142                    log("EF_CFIS: invalid data=" + IccUtils.bytesToHexString(data));
1143                }
1144                break;
1145
1146            case EVENT_GET_CSP_CPHS_DONE:
1147                isRecordLoadResponse = true;
1148
1149                ar = (AsyncResult)msg.obj;
1150
1151                if (ar.exception != null) {
1152                    loge("Exception in fetching EF_CSP data " + ar.exception);
1153                    break;
1154                }
1155
1156                data = (byte[])ar.result;
1157
1158                log("EF_CSP: " + IccUtils.bytesToHexString(data));
1159                handleEfCspData(data);
1160                break;
1161
1162            case EVENT_GET_GID1_DONE:
1163                isRecordLoadResponse = true;
1164
1165                ar = (AsyncResult)msg.obj;
1166                data =(byte[])ar.result;
1167
1168                if (ar.exception != null) {
1169                    loge("Exception in get GID1 " + ar.exception);
1170                    mGid1 = null;
1171                    break;
1172                }
1173                mGid1 = IccUtils.bytesToHexString(data);
1174                log("GID1: " + mGid1);
1175
1176                break;
1177
1178            default:
1179                super.handleMessage(msg);   // IccRecords handles generic record load responses
1180
1181        }}catch (RuntimeException exc) {
1182            // I don't want these exceptions to be fatal
1183            logw("Exception parsing SIM record", exc);
1184        } finally {
1185            // Count up record load responses even if they are fails
1186            if (isRecordLoadResponse) {
1187                onRecordLoaded();
1188            }
1189        }
1190    }
1191
1192    private void handleFileUpdate(int efid) {
1193        switch(efid) {
1194            case EF_MBDN:
1195                mRecordsToLoad++;
1196                new AdnRecordLoader(mFh).loadFromEF(EF_MBDN, EF_EXT6,
1197                        mMailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE));
1198                break;
1199            case EF_MAILBOX_CPHS:
1200                mRecordsToLoad++;
1201                new AdnRecordLoader(mFh).loadFromEF(EF_MAILBOX_CPHS, EF_EXT1,
1202                        1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
1203                break;
1204            case EF_CSP_CPHS:
1205                mRecordsToLoad++;
1206                log("[CSP] SIM Refresh for EF_CSP_CPHS");
1207                mFh.loadEFTransparent(EF_CSP_CPHS,
1208                        obtainMessage(EVENT_GET_CSP_CPHS_DONE));
1209                break;
1210            case EF_FDN:
1211                if (DBG) log("SIM Refresh called for EF_FDN");
1212                mParentApp.queryFdn();
1213                break;
1214            default:
1215                // For now, fetch all records if this is not a
1216                // voicemail number.
1217                // TODO: Handle other cases, instead of fetching all.
1218                mAdnCache.reset();
1219                fetchSimRecords();
1220                break;
1221        }
1222    }
1223
1224    private void handleSimRefresh(IccRefreshResponse refreshResponse){
1225        if (refreshResponse == null) {
1226            if (DBG) log("handleSimRefresh received without input");
1227            return;
1228        }
1229
1230        if (refreshResponse.aid != null &&
1231                !refreshResponse.aid.equals(mParentApp.getAid())) {
1232            // This is for different app. Ignore.
1233            return;
1234        }
1235
1236        switch (refreshResponse.refreshResult) {
1237            case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE:
1238                if (DBG) log("handleSimRefresh with SIM_FILE_UPDATED");
1239                handleFileUpdate(refreshResponse.efId);
1240                break;
1241            case IccRefreshResponse.REFRESH_RESULT_INIT:
1242                if (DBG) log("handleSimRefresh with SIM_REFRESH_INIT");
1243                // need to reload all files (that we care about)
1244                onIccRefreshInit();
1245                break;
1246            case IccRefreshResponse.REFRESH_RESULT_RESET:
1247                if (DBG) log("handleSimRefresh with SIM_REFRESH_RESET");
1248                if (requirePowerOffOnSimRefreshReset()) {
1249                    mCi.setRadioPower(false, null);
1250                    /* Note: no need to call setRadioPower(true).  Assuming the desired
1251                    * radio power state is still ON (as tracked by ServiceStateTracker),
1252                    * ServiceStateTracker will call setRadioPower when it receives the
1253                    * RADIO_STATE_CHANGED notification for the power off.  And if the
1254                    * desired power state has changed in the interim, we don't want to
1255                    * override it with an unconditional power on.
1256                    */
1257                }
1258                mAdnCache.reset();
1259                break;
1260            default:
1261                // unknown refresh operation
1262                if (DBG) log("handleSimRefresh with unknown operation");
1263                break;
1264        }
1265    }
1266
1267    /**
1268     * Dispatch 3GPP format message to registrant ({@code GSMPhone} or {@code CDMALTEPhone})
1269     * to pass to the 3GPP SMS dispatcher for delivery.
1270     */
1271    private int dispatchGsmMessage(SmsMessage message) {
1272        mNewSmsRegistrants.notifyResult(message);
1273        return 0;
1274    }
1275
1276    private void handleSms(byte[] ba) {
1277        if (ba[0] != 0)
1278            Rlog.d("ENF", "status : " + ba[0]);
1279
1280        // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1281        // 3 == "received by MS from network; message to be read"
1282        if (ba[0] == 3) {
1283            int n = ba.length;
1284
1285            // Note: Data may include trailing FF's.  That's OK; message
1286            // should still parse correctly.
1287            byte[] pdu = new byte[n - 1];
1288            System.arraycopy(ba, 1, pdu, 0, n - 1);
1289            SmsMessage message = SmsMessage.createFromPdu(pdu, SmsConstants.FORMAT_3GPP);
1290
1291            dispatchGsmMessage(message);
1292        }
1293    }
1294
1295
1296    private void handleSmses(ArrayList<byte[]> messages) {
1297        int count = messages.size();
1298
1299        for (int i = 0; i < count; i++) {
1300            byte[] ba = messages.get(i);
1301
1302            if (ba[0] != 0)
1303                Rlog.i("ENF", "status " + i + ": " + ba[0]);
1304
1305            // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1306            // 3 == "received by MS from network; message to be read"
1307
1308            if (ba[0] == 3) {
1309                int n = ba.length;
1310
1311                // Note: Data may include trailing FF's.  That's OK; message
1312                // should still parse correctly.
1313                byte[] pdu = new byte[n - 1];
1314                System.arraycopy(ba, 1, pdu, 0, n - 1);
1315                SmsMessage message = SmsMessage.createFromPdu(pdu, SmsConstants.FORMAT_3GPP);
1316
1317                dispatchGsmMessage(message);
1318
1319                // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1320                // 1 == "received by MS from network; message read"
1321
1322                ba[0] = 1;
1323
1324                if (false) { // FIXME: writing seems to crash RdoServD
1325                    mFh.updateEFLinearFixed(EF_SMS,
1326                            i, ba, null, obtainMessage(EVENT_MARK_SMS_READ_DONE, i));
1327                }
1328            }
1329        }
1330    }
1331
1332    @Override
1333    protected void onRecordLoaded() {
1334        // One record loaded successfully or failed, In either case
1335        // we need to update the recordsToLoad count
1336        mRecordsToLoad -= 1;
1337        if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested);
1338
1339        if (mRecordsToLoad == 0 && mRecordsRequested == true) {
1340            onAllRecordsLoaded();
1341        } else if (mRecordsToLoad < 0) {
1342            loge("recordsToLoad <0, programmer error suspected");
1343            mRecordsToLoad = 0;
1344        }
1345    }
1346
1347    @Override
1348    protected void onAllRecordsLoaded() {
1349        if (DBG) log("record load complete");
1350
1351        // Some fields require more than one SIM record to set
1352
1353        String operator = getOperatorNumeric();
1354        if (!TextUtils.isEmpty(operator)) {
1355            log("onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" +
1356                    operator + "'");
1357            log("update icc_operator_numeric=" + operator);
1358            setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, operator);
1359            final SubscriptionController subController = SubscriptionController.getInstance();
1360            subController.setMccMnc(operator, subController.getDefaultSmsSubId());
1361        } else {
1362            log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping");
1363        }
1364
1365        if (!TextUtils.isEmpty(mImsi)) {
1366            log("onAllRecordsLoaded set mcc imsi=" + mImsi);
1367            setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
1368                    MccTable.countryCodeForMcc(Integer.parseInt(mImsi.substring(0,3))));
1369        } else {
1370            log("onAllRecordsLoaded empty imsi skipping setting mcc");
1371        }
1372
1373        setVoiceMailByCountry(operator);
1374        setSpnFromConfig(operator);
1375
1376        mRecordsLoadedRegistrants.notifyRegistrants(
1377            new AsyncResult(null, null, null));
1378    }
1379
1380    //***** Private methods
1381
1382    private void setSpnFromConfig(String carrier) {
1383        if (mSpnOverride.containsCarrier(carrier)) {
1384            setServiceProviderName(mSpnOverride.getSpn(carrier));
1385        }
1386    }
1387
1388
1389    private void setVoiceMailByCountry (String spn) {
1390        if (mVmConfig.containsCarrier(spn)) {
1391            mIsVoiceMailFixed = true;
1392            mVoiceMailNum = mVmConfig.getVoiceMailNumber(spn);
1393            mVoiceMailTag = mVmConfig.getVoiceMailTag(spn);
1394        }
1395    }
1396
1397    @Override
1398    public void onReady() {
1399        fetchSimRecords();
1400    }
1401
1402    protected void fetchSimRecords() {
1403        mRecordsRequested = true;
1404
1405        if (DBG) log("fetchSimRecords " + mRecordsToLoad);
1406
1407        mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));
1408        mRecordsToLoad++;
1409
1410        mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
1411        mRecordsToLoad++;
1412
1413        // FIXME should examine EF[MSISDN]'s capability configuration
1414        // to determine which is the voice/data/fax line
1415        new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, EF_EXT1, 1,
1416                    obtainMessage(EVENT_GET_MSISDN_DONE));
1417        mRecordsToLoad++;
1418
1419        // Record number is subscriber profile
1420        mFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE));
1421        mRecordsToLoad++;
1422
1423        mFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE));
1424        mRecordsToLoad++;
1425
1426        // Record number is subscriber profile
1427        mFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE));
1428        mRecordsToLoad++;
1429
1430
1431        // Also load CPHS-style voice mail indicator, which stores
1432        // the same info as EF[MWIS]. If both exist, both are updated
1433        // but the EF[MWIS] data is preferred
1434        // Please note this must be loaded after EF[MWIS]
1435        mFh.loadEFTransparent(
1436                EF_VOICE_MAIL_INDICATOR_CPHS,
1437                obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE));
1438        mRecordsToLoad++;
1439
1440        // Same goes for Call Forward Status indicator: fetch both
1441        // EF[CFIS] and CPHS-EF, with EF[CFIS] preferred.
1442        mFh.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE));
1443        mRecordsToLoad++;
1444        mFh.loadEFTransparent(EF_CFF_CPHS, obtainMessage(EVENT_GET_CFF_DONE));
1445        mRecordsToLoad++;
1446
1447
1448        getSpnFsm(true, null);
1449
1450        mFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE));
1451        mRecordsToLoad++;
1452
1453        mFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE));
1454        mRecordsToLoad++;
1455
1456        mFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE));
1457        mRecordsToLoad++;
1458
1459        mFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE));
1460        mRecordsToLoad++;
1461
1462        mFh.loadEFTransparent(EF_CSP_CPHS,obtainMessage(EVENT_GET_CSP_CPHS_DONE));
1463        mRecordsToLoad++;
1464
1465        mFh.loadEFTransparent(EF_GID1, obtainMessage(EVENT_GET_GID1_DONE));
1466        mRecordsToLoad++;
1467
1468        // XXX should seek instead of examining them all
1469        if (false) { // XXX
1470            mFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE));
1471            mRecordsToLoad++;
1472        }
1473
1474        if (CRASH_RIL) {
1475            String sms = "0107912160130310f20404d0110041007030208054832b0120"
1476                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1477                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1478                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1479                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1480                         + "ffffffffffffffffffffffffffffff";
1481            byte[] ba = IccUtils.hexStringToBytes(sms);
1482
1483            mFh.updateEFLinearFixed(EF_SMS, 1, ba, null,
1484                            obtainMessage(EVENT_MARK_SMS_READ_DONE, 1));
1485        }
1486        if (DBG) log("fetchSimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested);
1487    }
1488
1489    /**
1490     * Returns the SpnDisplayRule based on settings on the SIM and the
1491     * specified plmn (currently-registered PLMN).  See TS 22.101 Annex A
1492     * and TS 51.011 10.3.11 for details.
1493     *
1494     * If the SPN is not found on the SIM or is empty, the rule is
1495     * always PLMN_ONLY.
1496     */
1497    @Override
1498    public int getDisplayRule(String plmn) {
1499        int rule;
1500
1501        if (mParentApp != null && mParentApp.getUiccCard() != null &&
1502            mParentApp.getUiccCard().getOperatorBrandOverride() != null) {
1503        // If the operator has been overridden, treat it as the SPN file on the SIM did not exist.
1504            rule = SPN_RULE_SHOW_PLMN;
1505        } else if (TextUtils.isEmpty(getServiceProviderName()) || mSpnDisplayCondition == -1) {
1506            // No EF_SPN content was found on the SIM, or not yet loaded.  Just show ONS.
1507            rule = SPN_RULE_SHOW_PLMN;
1508        } else if (isOnMatchingPlmn(plmn)) {
1509            rule = SPN_RULE_SHOW_SPN;
1510            if ((mSpnDisplayCondition & 0x01) == 0x01) {
1511                // ONS required when registered to HPLMN or PLMN in EF_SPDI
1512                rule |= SPN_RULE_SHOW_PLMN;
1513            }
1514        } else {
1515            rule = SPN_RULE_SHOW_PLMN;
1516            if ((mSpnDisplayCondition & 0x02) == 0x00) {
1517                // SPN required if not registered to HPLMN or PLMN in EF_SPDI
1518                rule |= SPN_RULE_SHOW_SPN;
1519            }
1520        }
1521        return rule;
1522    }
1523
1524    /**
1525     * Checks if plmn is HPLMN or on the spdiNetworks list.
1526     */
1527    private boolean isOnMatchingPlmn(String plmn) {
1528        if (plmn == null) return false;
1529
1530        if (plmn.equals(getOperatorNumeric())) {
1531            return true;
1532        }
1533
1534        if (mSpdiNetworks != null) {
1535            for (String spdiNet : mSpdiNetworks) {
1536                if (plmn.equals(spdiNet)) {
1537                    return true;
1538                }
1539            }
1540        }
1541        return false;
1542    }
1543
1544    /**
1545     * States of Get SPN Finite State Machine which only used by getSpnFsm()
1546     */
1547    private enum GetSpnFsmState {
1548        IDLE,               // No initialized
1549        INIT,               // Start FSM
1550        READ_SPN_3GPP,      // Load EF_SPN firstly
1551        READ_SPN_CPHS,      // Load EF_SPN_CPHS secondly
1552        READ_SPN_SHORT_CPHS // Load EF_SPN_SHORT_CPHS last
1553    }
1554
1555    /**
1556     * Finite State Machine to load Service Provider Name , which can be stored
1557     * in either EF_SPN (3GPP), EF_SPN_CPHS, or EF_SPN_SHORT_CPHS (CPHS4.2)
1558     *
1559     * After starting, FSM will search SPN EFs in order and stop after finding
1560     * the first valid SPN
1561     *
1562     * If the FSM gets restart while waiting for one of
1563     * SPN EFs results (i.e. a SIM refresh occurs after issuing
1564     * read EF_CPHS_SPN), it will re-initialize only after
1565     * receiving and discarding the unfinished SPN EF result.
1566     *
1567     * @param start set true only for initialize loading
1568     * @param ar the AsyncResult from loadEFTransparent
1569     *        ar.exception holds exception in error
1570     *        ar.result is byte[] for data in success
1571     */
1572    private void getSpnFsm(boolean start, AsyncResult ar) {
1573        byte[] data;
1574
1575        if (start) {
1576            // Check previous state to see if there is outstanding
1577            // SPN read
1578            if(mSpnState == GetSpnFsmState.READ_SPN_3GPP ||
1579               mSpnState == GetSpnFsmState.READ_SPN_CPHS ||
1580               mSpnState == GetSpnFsmState.READ_SPN_SHORT_CPHS ||
1581               mSpnState == GetSpnFsmState.INIT) {
1582                // Set INIT then return so the INIT code
1583                // will run when the outstanding read done.
1584                mSpnState = GetSpnFsmState.INIT;
1585                return;
1586            } else {
1587                mSpnState = GetSpnFsmState.INIT;
1588            }
1589        }
1590
1591        switch(mSpnState){
1592            case INIT:
1593                setServiceProviderName(null);
1594
1595                mFh.loadEFTransparent(EF_SPN,
1596                        obtainMessage(EVENT_GET_SPN_DONE));
1597                mRecordsToLoad++;
1598
1599                mSpnState = GetSpnFsmState.READ_SPN_3GPP;
1600                break;
1601            case READ_SPN_3GPP:
1602                if (ar != null && ar.exception == null) {
1603                    data = (byte[]) ar.result;
1604                    mSpnDisplayCondition = 0xff & data[0];
1605                    setServiceProviderName(IccUtils.adnStringFieldToString(
1606                            data, 1, data.length - 1));
1607
1608                    if (DBG) log("Load EF_SPN: " + getServiceProviderName()
1609                            + " spnDisplayCondition: " + mSpnDisplayCondition);
1610                    setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, getServiceProviderName());
1611
1612                    mSpnState = GetSpnFsmState.IDLE;
1613                } else {
1614                    mFh.loadEFTransparent( EF_SPN_CPHS,
1615                            obtainMessage(EVENT_GET_SPN_DONE));
1616                    mRecordsToLoad++;
1617
1618                    mSpnState = GetSpnFsmState.READ_SPN_CPHS;
1619
1620                    // See TS 51.011 10.3.11.  Basically, default to
1621                    // show PLMN always, and SPN also if roaming.
1622                    mSpnDisplayCondition = -1;
1623                }
1624                break;
1625            case READ_SPN_CPHS:
1626                if (ar != null && ar.exception == null) {
1627                    data = (byte[]) ar.result;
1628                    setServiceProviderName(IccUtils.adnStringFieldToString(data, 0, data.length));
1629
1630                    if (DBG) log("Load EF_SPN_CPHS: " + getServiceProviderName());
1631                    setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, getServiceProviderName());
1632
1633                    mSpnState = GetSpnFsmState.IDLE;
1634                } else {
1635                    mFh.loadEFTransparent(
1636                            EF_SPN_SHORT_CPHS, obtainMessage(EVENT_GET_SPN_DONE));
1637                    mRecordsToLoad++;
1638
1639                    mSpnState = GetSpnFsmState.READ_SPN_SHORT_CPHS;
1640                }
1641                break;
1642            case READ_SPN_SHORT_CPHS:
1643                if (ar != null && ar.exception == null) {
1644                    data = (byte[]) ar.result;
1645                    setServiceProviderName(IccUtils.adnStringFieldToString(data, 0, data.length));
1646
1647                    if (DBG) log("Load EF_SPN_SHORT_CPHS: " + getServiceProviderName());
1648                    setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, getServiceProviderName());
1649                }else {
1650                    if (DBG) log("No SPN loaded in either CHPS or 3GPP");
1651                }
1652
1653                mSpnState = GetSpnFsmState.IDLE;
1654                break;
1655            default:
1656                mSpnState = GetSpnFsmState.IDLE;
1657        }
1658    }
1659
1660    /**
1661     * Parse TS 51.011 EF[SPDI] record
1662     * This record contains the list of numeric network IDs that
1663     * are treated specially when determining SPN display
1664     */
1665    private void
1666    parseEfSpdi(byte[] data) {
1667        SimTlv tlv = new SimTlv(data, 0, data.length);
1668
1669        byte[] plmnEntries = null;
1670
1671        for ( ; tlv.isValidObject() ; tlv.nextObject()) {
1672            // Skip SPDI tag, if existant
1673            if (tlv.getTag() == TAG_SPDI) {
1674              tlv = new SimTlv(tlv.getData(), 0, tlv.getData().length);
1675            }
1676            // There should only be one TAG_SPDI_PLMN_LIST
1677            if (tlv.getTag() == TAG_SPDI_PLMN_LIST) {
1678                plmnEntries = tlv.getData();
1679                break;
1680            }
1681        }
1682
1683        if (plmnEntries == null) {
1684            return;
1685        }
1686
1687        mSpdiNetworks = new ArrayList<String>(plmnEntries.length / 3);
1688
1689        for (int i = 0 ; i + 2 < plmnEntries.length ; i += 3) {
1690            String plmnCode;
1691            plmnCode = IccUtils.bcdToString(plmnEntries, i, 3);
1692
1693            // Valid operator codes are 5 or 6 digits
1694            if (plmnCode.length() >= 5) {
1695                log("EF_SPDI network: " + plmnCode);
1696                mSpdiNetworks.add(plmnCode);
1697            }
1698        }
1699    }
1700
1701    /**
1702     * check to see if Mailbox Number is allocated and activated in CPHS SST
1703     */
1704    private boolean isCphsMailboxEnabled() {
1705        if (mCphsInfo == null)  return false;
1706        return ((mCphsInfo[1] & CPHS_SST_MBN_MASK) == CPHS_SST_MBN_ENABLED );
1707    }
1708
1709    @Override
1710    protected void log(String s) {
1711        Rlog.d(LOG_TAG, "[SIMRecords] " + s);
1712    }
1713
1714    @Override
1715    protected void loge(String s) {
1716        Rlog.e(LOG_TAG, "[SIMRecords] " + s);
1717    }
1718
1719    protected void logw(String s, Throwable tr) {
1720        Rlog.w(LOG_TAG, "[SIMRecords] " + s, tr);
1721    }
1722
1723    protected void logv(String s) {
1724        Rlog.v(LOG_TAG, "[SIMRecords] " + s);
1725    }
1726
1727    /**
1728     * Return true if "Restriction of menu options for manual PLMN selection"
1729     * bit is set or EF_CSP data is unavailable, return false otherwise.
1730     */
1731    @Override
1732    public boolean isCspPlmnEnabled() {
1733        return mCspPlmnEnabled;
1734    }
1735
1736    /**
1737     * Parse EF_CSP data and check if
1738     * "Restriction of menu options for manual PLMN selection" is
1739     * Enabled/Disabled
1740     *
1741     * @param data EF_CSP hex data.
1742     */
1743    private void handleEfCspData(byte[] data) {
1744        // As per spec CPHS4_2.WW6, CPHS B.4.7.1, EF_CSP contains CPHS defined
1745        // 18 bytes (i.e 9 service groups info) and additional data specific to
1746        // operator. The valueAddedServicesGroup is not part of standard
1747        // services. This is operator specific and can be programmed any where.
1748        // Normally this is programmed as 10th service after the standard
1749        // services.
1750        int usedCspGroups = data.length / 2;
1751        // This is the "Service Group Number" of "Value Added Services Group".
1752        byte valueAddedServicesGroup = (byte)0xC0;
1753
1754        mCspPlmnEnabled = true;
1755        for (int i = 0; i < usedCspGroups; i++) {
1756             if (data[2 * i] == valueAddedServicesGroup) {
1757                 log("[CSP] found ValueAddedServicesGroup, value " + data[(2 * i) + 1]);
1758                 if ((data[(2 * i) + 1] & 0x80) == 0x80) {
1759                     // Bit 8 is for
1760                     // "Restriction of menu options for manual PLMN selection".
1761                     // Operator Selection menu should be enabled.
1762                     mCspPlmnEnabled = true;
1763                 } else {
1764                     mCspPlmnEnabled = false;
1765                     // Operator Selection menu should be disabled.
1766                     // Operator Selection Mode should be set to Automatic.
1767                     log("[CSP] Set Automatic Network Selection");
1768                     mNetworkSelectionModeAutomaticRegistrants.notifyRegistrants();
1769                 }
1770                 return;
1771             }
1772        }
1773
1774        log("[CSP] Value Added Service Group (0xC0), not found!");
1775    }
1776
1777    @Override
1778    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1779        pw.println("SIMRecords: " + this);
1780        pw.println(" extends:");
1781        super.dump(fd, pw, args);
1782        pw.println(" mVmConfig=" + mVmConfig);
1783        pw.println(" mSpnOverride=" + mSpnOverride);
1784        pw.println(" mCallForwardingEnabled=" + mCallForwardingEnabled);
1785        pw.println(" mSpnState=" + mSpnState);
1786        pw.println(" mCphsInfo=" + mCphsInfo);
1787        pw.println(" mCspPlmnEnabled=" + mCspPlmnEnabled);
1788        pw.println(" mEfMWIS[]=" + Arrays.toString(mEfMWIS));
1789        pw.println(" mEfCPHS_MWI[]=" + Arrays.toString(mEfCPHS_MWI));
1790        pw.println(" mEfCff[]=" + Arrays.toString(mEfCff));
1791        pw.println(" mEfCfis[]=" + Arrays.toString(mEfCfis));
1792        pw.println(" mSpnDisplayCondition=" + mSpnDisplayCondition);
1793        pw.println(" mSpdiNetworks[]=" + mSpdiNetworks);
1794        pw.println(" mPnnHomeName=" + mPnnHomeName);
1795        pw.println(" mUsimServiceTable=" + mUsimServiceTable);
1796        pw.println(" mGid1=" + mGid1);
1797        pw.flush();
1798    }
1799
1800    private void setSystemProperty(String key, String val) {
1801        // Update the system properties only in case NON-DSDS.
1802        // TODO: Shall have a better approach!
1803        if (!TelephonyManager.getDefault().isMultiSimEnabled()) {
1804            SystemProperties.set(key, val);
1805        }
1806    }
1807}
1808