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