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