[go: nahoru, domu]

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