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