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