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