SIMRecords.java revision 8c4891c01bd59bee7a1cdf1f992e6078896bea00
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            SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, getServiceProviderName());
1491        }
1492    }
1493
1494
1495    private void setVoiceMailByCountry (String spn) {
1496        if (mVmConfig.containsCarrier(spn)) {
1497            mIsVoiceMailFixed = true;
1498            mVoiceMailNum = mVmConfig.getVoiceMailNumber(spn);
1499            mVoiceMailTag = mVmConfig.getVoiceMailTag(spn);
1500        }
1501    }
1502
1503    @Override
1504    public void onReady() {
1505        fetchSimRecords();
1506    }
1507
1508    private void onLocked() {
1509        if (DBG) log("only fetch EF_LI and EF_PL in lock state");
1510        loadEfLiAndEfPl();
1511    }
1512
1513    private void loadEfLiAndEfPl() {
1514        if (mParentApp.getType() == AppType.APPTYPE_USIM) {
1515            mRecordsRequested = true;
1516            mFh.loadEFTransparent(EF_LI,
1517                    obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfUsimLiLoaded()));
1518            mRecordsToLoad++;
1519
1520            mFh.loadEFTransparent(EF_PL,
1521                    obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfPlLoaded()));
1522            mRecordsToLoad++;
1523        }
1524    }
1525
1526    protected void fetchSimRecords() {
1527        mRecordsRequested = true;
1528
1529        if (DBG) log("fetchSimRecords " + mRecordsToLoad);
1530
1531        mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));
1532        mRecordsToLoad++;
1533
1534        mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
1535        mRecordsToLoad++;
1536
1537        // FIXME should examine EF[MSISDN]'s capability configuration
1538        // to determine which is the voice/data/fax line
1539        new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, EF_EXT1, 1,
1540                    obtainMessage(EVENT_GET_MSISDN_DONE));
1541        mRecordsToLoad++;
1542
1543        // Record number is subscriber profile
1544        mFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE));
1545        mRecordsToLoad++;
1546
1547        mFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE));
1548        mRecordsToLoad++;
1549
1550        // Record number is subscriber profile
1551        mFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE));
1552        mRecordsToLoad++;
1553
1554
1555        // Also load CPHS-style voice mail indicator, which stores
1556        // the same info as EF[MWIS]. If both exist, both are updated
1557        // but the EF[MWIS] data is preferred
1558        // Please note this must be loaded after EF[MWIS]
1559        mFh.loadEFTransparent(
1560                EF_VOICE_MAIL_INDICATOR_CPHS,
1561                obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE));
1562        mRecordsToLoad++;
1563
1564        // Same goes for Call Forward Status indicator: fetch both
1565        // EF[CFIS] and CPHS-EF, with EF[CFIS] preferred.
1566        mFh.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE));
1567        mRecordsToLoad++;
1568        mFh.loadEFTransparent(EF_CFF_CPHS, obtainMessage(EVENT_GET_CFF_DONE));
1569        mRecordsToLoad++;
1570
1571
1572        getSpnFsm(true, null);
1573
1574        mFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE));
1575        mRecordsToLoad++;
1576
1577        mFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE));
1578        mRecordsToLoad++;
1579
1580        mFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE));
1581        mRecordsToLoad++;
1582
1583        mFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE));
1584        mRecordsToLoad++;
1585
1586        mFh.loadEFTransparent(EF_CSP_CPHS,obtainMessage(EVENT_GET_CSP_CPHS_DONE));
1587        mRecordsToLoad++;
1588
1589        mFh.loadEFTransparent(EF_GID1, obtainMessage(EVENT_GET_GID1_DONE));
1590        mRecordsToLoad++;
1591
1592        loadEfLiAndEfPl();
1593
1594        // XXX should seek instead of examining them all
1595        if (false) { // XXX
1596            mFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE));
1597            mRecordsToLoad++;
1598        }
1599
1600        if (CRASH_RIL) {
1601            String sms = "0107912160130310f20404d0110041007030208054832b0120"
1602                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1603                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1604                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1605                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1606                         + "ffffffffffffffffffffffffffffff";
1607            byte[] ba = IccUtils.hexStringToBytes(sms);
1608
1609            mFh.updateEFLinearFixed(EF_SMS, 1, ba, null,
1610                            obtainMessage(EVENT_MARK_SMS_READ_DONE, 1));
1611        }
1612        if (DBG) log("fetchSimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested);
1613    }
1614
1615    /**
1616     * Returns the SpnDisplayRule based on settings on the SIM and the
1617     * specified plmn (currently-registered PLMN).  See TS 22.101 Annex A
1618     * and TS 51.011 10.3.11 for details.
1619     *
1620     * If the SPN is not found on the SIM or is empty, the rule is
1621     * always PLMN_ONLY.
1622     */
1623    @Override
1624    public int getDisplayRule(String plmn) {
1625        int rule;
1626
1627        if (mParentApp != null && mParentApp.getUiccCard() != null &&
1628            mParentApp.getUiccCard().getOperatorBrandOverride() != null) {
1629        // If the operator has been overridden, treat it as the SPN file on the SIM did not exist.
1630            rule = SPN_RULE_SHOW_PLMN;
1631        } else if (TextUtils.isEmpty(getServiceProviderName()) || mSpnDisplayCondition == -1) {
1632            // No EF_SPN content was found on the SIM, or not yet loaded.  Just show ONS.
1633            rule = SPN_RULE_SHOW_PLMN;
1634        } else if (isOnMatchingPlmn(plmn)) {
1635            rule = SPN_RULE_SHOW_SPN;
1636            if ((mSpnDisplayCondition & 0x01) == 0x01) {
1637                // ONS required when registered to HPLMN or PLMN in EF_SPDI
1638                rule |= SPN_RULE_SHOW_PLMN;
1639            }
1640        } else {
1641            rule = SPN_RULE_SHOW_PLMN;
1642            if ((mSpnDisplayCondition & 0x02) == 0x00) {
1643                // SPN required if not registered to HPLMN or PLMN in EF_SPDI
1644                rule |= SPN_RULE_SHOW_SPN;
1645            }
1646        }
1647        return rule;
1648    }
1649
1650    /**
1651     * Checks if plmn is HPLMN or on the spdiNetworks list.
1652     */
1653    private boolean isOnMatchingPlmn(String plmn) {
1654        if (plmn == null) return false;
1655
1656        if (plmn.equals(getOperatorNumeric())) {
1657            return true;
1658        }
1659
1660        if (mSpdiNetworks != null) {
1661            for (String spdiNet : mSpdiNetworks) {
1662                if (plmn.equals(spdiNet)) {
1663                    return true;
1664                }
1665            }
1666        }
1667        return false;
1668    }
1669
1670    /**
1671     * States of Get SPN Finite State Machine which only used by getSpnFsm()
1672     */
1673    private enum GetSpnFsmState {
1674        IDLE,               // No initialized
1675        INIT,               // Start FSM
1676        READ_SPN_3GPP,      // Load EF_SPN firstly
1677        READ_SPN_CPHS,      // Load EF_SPN_CPHS secondly
1678        READ_SPN_SHORT_CPHS // Load EF_SPN_SHORT_CPHS last
1679    }
1680
1681    /**
1682     * Finite State Machine to load Service Provider Name , which can be stored
1683     * in either EF_SPN (3GPP), EF_SPN_CPHS, or EF_SPN_SHORT_CPHS (CPHS4.2)
1684     *
1685     * After starting, FSM will search SPN EFs in order and stop after finding
1686     * the first valid SPN
1687     *
1688     * If the FSM gets restart while waiting for one of
1689     * SPN EFs results (i.e. a SIM refresh occurs after issuing
1690     * read EF_CPHS_SPN), it will re-initialize only after
1691     * receiving and discarding the unfinished SPN EF result.
1692     *
1693     * @param start set true only for initialize loading
1694     * @param ar the AsyncResult from loadEFTransparent
1695     *        ar.exception holds exception in error
1696     *        ar.result is byte[] for data in success
1697     */
1698    private void getSpnFsm(boolean start, AsyncResult ar) {
1699        byte[] data;
1700
1701        if (start) {
1702            // Check previous state to see if there is outstanding
1703            // SPN read
1704            if(mSpnState == GetSpnFsmState.READ_SPN_3GPP ||
1705               mSpnState == GetSpnFsmState.READ_SPN_CPHS ||
1706               mSpnState == GetSpnFsmState.READ_SPN_SHORT_CPHS ||
1707               mSpnState == GetSpnFsmState.INIT) {
1708                // Set INIT then return so the INIT code
1709                // will run when the outstanding read done.
1710                mSpnState = GetSpnFsmState.INIT;
1711                return;
1712            } else {
1713                mSpnState = GetSpnFsmState.INIT;
1714            }
1715        }
1716
1717        switch(mSpnState){
1718            case INIT:
1719                setServiceProviderName(null);
1720
1721                mFh.loadEFTransparent(EF_SPN,
1722                        obtainMessage(EVENT_GET_SPN_DONE));
1723                mRecordsToLoad++;
1724
1725                mSpnState = GetSpnFsmState.READ_SPN_3GPP;
1726                break;
1727            case READ_SPN_3GPP:
1728                if (ar != null && ar.exception == null) {
1729                    data = (byte[]) ar.result;
1730                    mSpnDisplayCondition = 0xff & data[0];
1731                    setServiceProviderName(IccUtils.adnStringFieldToString(
1732                            data, 1, data.length - 1));
1733
1734                    if (DBG) log("Load EF_SPN: " + getServiceProviderName()
1735                            + " spnDisplayCondition: " + mSpnDisplayCondition);
1736                    setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, getServiceProviderName());
1737
1738                    mSpnState = GetSpnFsmState.IDLE;
1739                } else {
1740                    mFh.loadEFTransparent( EF_SPN_CPHS,
1741                            obtainMessage(EVENT_GET_SPN_DONE));
1742                    mRecordsToLoad++;
1743
1744                    mSpnState = GetSpnFsmState.READ_SPN_CPHS;
1745
1746                    // See TS 51.011 10.3.11.  Basically, default to
1747                    // show PLMN always, and SPN also if roaming.
1748                    mSpnDisplayCondition = -1;
1749                }
1750                break;
1751            case READ_SPN_CPHS:
1752                if (ar != null && ar.exception == null) {
1753                    data = (byte[]) ar.result;
1754                    setServiceProviderName(IccUtils.adnStringFieldToString(data, 0, data.length));
1755
1756                    if (DBG) log("Load EF_SPN_CPHS: " + getServiceProviderName());
1757                    setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, getServiceProviderName());
1758
1759                    mSpnState = GetSpnFsmState.IDLE;
1760                } else {
1761                    mFh.loadEFTransparent(
1762                            EF_SPN_SHORT_CPHS, obtainMessage(EVENT_GET_SPN_DONE));
1763                    mRecordsToLoad++;
1764
1765                    mSpnState = GetSpnFsmState.READ_SPN_SHORT_CPHS;
1766                }
1767                break;
1768            case READ_SPN_SHORT_CPHS:
1769                if (ar != null && ar.exception == null) {
1770                    data = (byte[]) ar.result;
1771                    setServiceProviderName(IccUtils.adnStringFieldToString(data, 0, data.length));
1772
1773                    if (DBG) log("Load EF_SPN_SHORT_CPHS: " + getServiceProviderName());
1774                    setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, getServiceProviderName());
1775                }else {
1776                    if (DBG) log("No SPN loaded in either CHPS or 3GPP");
1777                }
1778
1779                mSpnState = GetSpnFsmState.IDLE;
1780                break;
1781            default:
1782                mSpnState = GetSpnFsmState.IDLE;
1783        }
1784    }
1785
1786    /**
1787     * Parse TS 51.011 EF[SPDI] record
1788     * This record contains the list of numeric network IDs that
1789     * are treated specially when determining SPN display
1790     */
1791    private void
1792    parseEfSpdi(byte[] data) {
1793        SimTlv tlv = new SimTlv(data, 0, data.length);
1794
1795        byte[] plmnEntries = null;
1796
1797        for ( ; tlv.isValidObject() ; tlv.nextObject()) {
1798            // Skip SPDI tag, if existant
1799            if (tlv.getTag() == TAG_SPDI) {
1800              tlv = new SimTlv(tlv.getData(), 0, tlv.getData().length);
1801            }
1802            // There should only be one TAG_SPDI_PLMN_LIST
1803            if (tlv.getTag() == TAG_SPDI_PLMN_LIST) {
1804                plmnEntries = tlv.getData();
1805                break;
1806            }
1807        }
1808
1809        if (plmnEntries == null) {
1810            return;
1811        }
1812
1813        mSpdiNetworks = new ArrayList<String>(plmnEntries.length / 3);
1814
1815        for (int i = 0 ; i + 2 < plmnEntries.length ; i += 3) {
1816            String plmnCode;
1817            plmnCode = IccUtils.bcdToString(plmnEntries, i, 3);
1818
1819            // Valid operator codes are 5 or 6 digits
1820            if (plmnCode.length() >= 5) {
1821                log("EF_SPDI network: " + plmnCode);
1822                mSpdiNetworks.add(plmnCode);
1823            }
1824        }
1825    }
1826
1827    /**
1828     * check to see if Mailbox Number is allocated and activated in CPHS SST
1829     */
1830    private boolean isCphsMailboxEnabled() {
1831        if (mCphsInfo == null)  return false;
1832        return ((mCphsInfo[1] & CPHS_SST_MBN_MASK) == CPHS_SST_MBN_ENABLED );
1833    }
1834
1835    @Override
1836    protected void log(String s) {
1837        Rlog.d(LOG_TAG, "[SIMRecords] " + s);
1838    }
1839
1840    @Override
1841    protected void loge(String s) {
1842        Rlog.e(LOG_TAG, "[SIMRecords] " + s);
1843    }
1844
1845    protected void logw(String s, Throwable tr) {
1846        Rlog.w(LOG_TAG, "[SIMRecords] " + s, tr);
1847    }
1848
1849    protected void logv(String s) {
1850        Rlog.v(LOG_TAG, "[SIMRecords] " + s);
1851    }
1852
1853    /**
1854     * Return true if "Restriction of menu options for manual PLMN selection"
1855     * bit is set or EF_CSP data is unavailable, return false otherwise.
1856     */
1857    @Override
1858    public boolean isCspPlmnEnabled() {
1859        return mCspPlmnEnabled;
1860    }
1861
1862    /**
1863     * Parse EF_CSP data and check if
1864     * "Restriction of menu options for manual PLMN selection" is
1865     * Enabled/Disabled
1866     *
1867     * @param data EF_CSP hex data.
1868     */
1869    private void handleEfCspData(byte[] data) {
1870        // As per spec CPHS4_2.WW6, CPHS B.4.7.1, EF_CSP contains CPHS defined
1871        // 18 bytes (i.e 9 service groups info) and additional data specific to
1872        // operator. The valueAddedServicesGroup is not part of standard
1873        // services. This is operator specific and can be programmed any where.
1874        // Normally this is programmed as 10th service after the standard
1875        // services.
1876        int usedCspGroups = data.length / 2;
1877        // This is the "Service Group Number" of "Value Added Services Group".
1878        byte valueAddedServicesGroup = (byte)0xC0;
1879
1880        mCspPlmnEnabled = true;
1881        for (int i = 0; i < usedCspGroups; i++) {
1882             if (data[2 * i] == valueAddedServicesGroup) {
1883                 log("[CSP] found ValueAddedServicesGroup, value " + data[(2 * i) + 1]);
1884                 if ((data[(2 * i) + 1] & 0x80) == 0x80) {
1885                     // Bit 8 is for
1886                     // "Restriction of menu options for manual PLMN selection".
1887                     // Operator Selection menu should be enabled.
1888                     mCspPlmnEnabled = true;
1889                 } else {
1890                     mCspPlmnEnabled = false;
1891                     // Operator Selection menu should be disabled.
1892                     // Operator Selection Mode should be set to Automatic.
1893                     log("[CSP] Set Automatic Network Selection");
1894                     mNetworkSelectionModeAutomaticRegistrants.notifyRegistrants();
1895                 }
1896                 return;
1897             }
1898        }
1899
1900        log("[CSP] Value Added Service Group (0xC0), not found!");
1901    }
1902
1903    @Override
1904    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1905        pw.println("SIMRecords: " + this);
1906        pw.println(" extends:");
1907        super.dump(fd, pw, args);
1908        pw.println(" mVmConfig=" + mVmConfig);
1909        pw.println(" mSpnOverride=" + mSpnOverride);
1910        pw.println(" mCallForwardingEnabled=" + mCallForwardingEnabled);
1911        pw.println(" mSpnState=" + mSpnState);
1912        pw.println(" mCphsInfo=" + mCphsInfo);
1913        pw.println(" mCspPlmnEnabled=" + mCspPlmnEnabled);
1914        pw.println(" mEfMWIS[]=" + Arrays.toString(mEfMWIS));
1915        pw.println(" mEfCPHS_MWI[]=" + Arrays.toString(mEfCPHS_MWI));
1916        pw.println(" mEfCff[]=" + Arrays.toString(mEfCff));
1917        pw.println(" mEfCfis[]=" + Arrays.toString(mEfCfis));
1918        pw.println(" mSpnDisplayCondition=" + mSpnDisplayCondition);
1919        pw.println(" mSpdiNetworks[]=" + mSpdiNetworks);
1920        pw.println(" mPnnHomeName=" + mPnnHomeName);
1921        pw.println(" mUsimServiceTable=" + mUsimServiceTable);
1922        pw.println(" mGid1=" + mGid1);
1923        pw.flush();
1924    }
1925
1926    private void setSystemProperty(String key, String val) {
1927        // Update the system properties only in case NON-DSDS.
1928        // TODO: Shall have a better approach!
1929        if (!TelephonyManager.getDefault().isMultiSimEnabled()) {
1930            SystemProperties.set(key, val);
1931        }
1932    }
1933}
1934