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