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