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