SIMRecords.java revision 1f3278e111c8abadedf71a9bcdbbaffe154be738
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.gsm;
18
19import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
20import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
21import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
22import android.content.Context;
23import android.os.AsyncResult;
24import android.os.Message;
25import android.os.SystemProperties;
26import android.util.Log;
27
28import com.android.internal.telephony.AdnRecord;
29import com.android.internal.telephony.AdnRecordCache;
30import com.android.internal.telephony.AdnRecordLoader;
31import com.android.internal.telephony.CommandsInterface;
32import com.android.internal.telephony.IccFileHandler;
33import com.android.internal.telephony.IccRecords;
34import com.android.internal.telephony.IccUtils;
35import com.android.internal.telephony.IccVmFixedException;
36import com.android.internal.telephony.IccVmNotSupportedException;
37import com.android.internal.telephony.MccTable;
38import com.android.internal.telephony.PhoneBase;
39
40import java.util.ArrayList;
41
42
43/**
44 * {@hide}
45 */
46public final class SIMRecords extends IccRecords {
47    static final String LOG_TAG = "GSM";
48
49    private static final boolean CRASH_RIL = false;
50
51    private static final boolean DBG = true;
52
53    // ***** Instance Variables
54
55    VoiceMailConstants mVmConfig;
56
57
58    SpnOverride mSpnOverride;
59
60    // ***** Cached SIM State; cleared on channel close
61
62    private String imsi;
63    private boolean callForwardingEnabled;
64
65
66    /**
67     * States only used by getSpnFsm FSM
68     */
69    private Get_Spn_Fsm_State spnState;
70
71    /** CPHS service information (See CPHS 4.2 B.3.1.1)
72     *  It will be set in onSimReady if reading GET_CPHS_INFO successfully
73     *  mCphsInfo[0] is CPHS Phase
74     *  mCphsInfo[1] and mCphsInfo[2] is CPHS Service Table
75     */
76    private byte[] mCphsInfo = null;
77    boolean mCspPlmnEnabled = true;
78
79    byte[] efMWIS = null;
80    byte[] efCPHS_MWI =null;
81    byte[] mEfCff = null;
82    byte[] mEfCfis = null;
83
84
85    int spnDisplayCondition;
86    // Numeric network codes listed in TS 51.011 EF[SPDI]
87    ArrayList<String> spdiNetworks = null;
88
89    String pnnHomeName = null;
90
91    // ***** Constants
92
93    // Bitmasks for SPN display rules.
94    static final int SPN_RULE_SHOW_SPN  = 0x01;
95    static final int SPN_RULE_SHOW_PLMN = 0x02;
96
97    // From TS 51.011 EF[SPDI] section
98    static final int TAG_SPDI = 0xA3;
99    static final int TAG_SPDI_PLMN_LIST = 0x80;
100
101    // Full Name IEI from TS 24.008
102    static final int TAG_FULL_NETWORK_NAME = 0x43;
103
104    // Short Name IEI from TS 24.008
105    static final int TAG_SHORT_NETWORK_NAME = 0x45;
106
107    // active CFF from CPHS 4.2 B.4.5
108    static final int CFF_UNCONDITIONAL_ACTIVE = 0x0a;
109    static final int CFF_UNCONDITIONAL_DEACTIVE = 0x05;
110    static final int CFF_LINE1_MASK = 0x0f;
111    static final int CFF_LINE1_RESET = 0xf0;
112
113    // CPHS Service Table (See CPHS 4.2 B.3.1)
114    private static final int CPHS_SST_MBN_MASK = 0x30;
115    private static final int CPHS_SST_MBN_ENABLED = 0x30;
116
117    // ***** Event Constants
118
119    private static final int EVENT_SIM_READY = 1;
120    private static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 2;
121    private static final int EVENT_GET_IMSI_DONE = 3;
122    private static final int EVENT_GET_ICCID_DONE = 4;
123    private static final int EVENT_GET_MBI_DONE = 5;
124    private static final int EVENT_GET_MBDN_DONE = 6;
125    private static final int EVENT_GET_MWIS_DONE = 7;
126    private static final int EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE = 8;
127    private static final int EVENT_GET_AD_DONE = 9; // Admin data on SIM
128    private static final int EVENT_GET_MSISDN_DONE = 10;
129    private static final int EVENT_GET_CPHS_MAILBOX_DONE = 11;
130    private static final int EVENT_GET_SPN_DONE = 12;
131    private static final int EVENT_GET_SPDI_DONE = 13;
132    private static final int EVENT_UPDATE_DONE = 14;
133    private static final int EVENT_GET_PNN_DONE = 15;
134    private static final int EVENT_GET_SST_DONE = 17;
135    private static final int EVENT_GET_ALL_SMS_DONE = 18;
136    private static final int EVENT_MARK_SMS_READ_DONE = 19;
137    private static final int EVENT_SET_MBDN_DONE = 20;
138    private static final int EVENT_SMS_ON_SIM = 21;
139    private static final int EVENT_GET_SMS_DONE = 22;
140    private static final int EVENT_GET_CFF_DONE = 24;
141    private static final int EVENT_SET_CPHS_MAILBOX_DONE = 25;
142    private static final int EVENT_GET_INFO_CPHS_DONE = 26;
143    private static final int EVENT_SET_MSISDN_DONE = 30;
144    private static final int EVENT_SIM_REFRESH = 31;
145    private static final int EVENT_GET_CFIS_DONE = 32;
146    private static final int EVENT_GET_CSP_CPHS_DONE = 33;
147
148    // Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length.
149
150    private static final String[] MCCMNC_CODES_HAVING_3DIGITS_MNC = {
151        "405025", "405026", "405027", "405028", "405029", "405030", "405031", "405032",
152        "405033", "405034", "405035", "405036", "405037", "405038", "405039", "405040",
153        "405041", "405042", "405043", "405044", "405045", "405046", "405047", "405750",
154        "405751", "405752", "405753", "405754", "405755", "405756", "405799", "405800",
155        "405801", "405802", "405803", "405804", "405805", "405806", "405807", "405808",
156        "405809", "405810", "405811", "405812", "405813", "405814", "405815", "405816",
157        "405817", "405818", "405819", "405820", "405821", "405822", "405823", "405824",
158        "405825", "405826", "405827", "405828", "405829", "405830", "405831", "405832",
159        "405833", "405834", "405835", "405836", "405837", "405838", "405839", "405840",
160        "405841", "405842", "405843", "405844", "405845", "405846", "405847", "405848",
161        "405849", "405850", "405851", "405852", "405853", "405875", "405876", "405877",
162        "405878", "405879", "405880", "405881", "405882", "405883", "405884", "405885",
163        "405886", "405908", "405909", "405910", "405911", "405925", "405926", "405927",
164        "405928", "405929", "405932"
165    };
166
167    // ***** Constructor
168
169    public SIMRecords(PhoneBase p) {
170        super(p);
171
172        adnCache = new AdnRecordCache(phone);
173
174        mVmConfig = new VoiceMailConstants();
175        mSpnOverride = new SpnOverride();
176
177        recordsRequested = false;  // No load request is made till SIM ready
178
179        // recordsToLoad is set to 0 because no requests are made yet
180        recordsToLoad = 0;
181
182
183        p.mCM.registerForSIMReady(this, EVENT_SIM_READY, null);
184        p.mCM.registerForOffOrNotAvailable(
185                        this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
186        p.mCM.setOnSmsOnSim(this, EVENT_SMS_ON_SIM, null);
187        p.mCM.setOnIccRefresh(this, EVENT_SIM_REFRESH, null);
188
189        // Start off by setting empty state
190        onRadioOffOrNotAvailable();
191
192    }
193
194    @Override
195    public void dispose() {
196        //Unregister for all events
197        phone.mCM.unregisterForSIMReady(this);
198        phone.mCM.unregisterForOffOrNotAvailable( this);
199        phone.mCM.unSetOnIccRefresh(this);
200    }
201
202    protected void finalize() {
203        if(DBG) Log.d(LOG_TAG, "SIMRecords finalized");
204    }
205
206    protected void onRadioOffOrNotAvailable() {
207        imsi = null;
208        msisdn = null;
209        voiceMailNum = null;
210        countVoiceMessages = 0;
211        mncLength = UNINITIALIZED;
212        iccid = null;
213        // -1 means no EF_SPN found; treat accordingly.
214        spnDisplayCondition = -1;
215        efMWIS = null;
216        efCPHS_MWI = null;
217        spdiNetworks = null;
218        pnnHomeName = null;
219
220        adnCache.reset();
221
222        phone.setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, null);
223        phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, null);
224        phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, null);
225
226        // recordsRequested is set to false indicating that the SIM
227        // read requests made so far are not valid. This is set to
228        // true only when fresh set of read requests are made.
229        recordsRequested = false;
230    }
231
232
233    //***** Public Methods
234
235    /**
236     * {@inheritDoc}
237     */
238    @Override
239    public String getIMSI() {
240        return imsi;
241    }
242
243    public String getMsisdnNumber() {
244        return msisdn;
245    }
246
247    /**
248     * Set subscriber number to SIM record
249     *
250     * The subscriber number is stored in EF_MSISDN (TS 51.011)
251     *
252     * When the operation is complete, onComplete will be sent to its handler
253     *
254     * @param alphaTag alpha-tagging of the dailing nubmer (up to 10 characters)
255     * @param number dailing nubmer (up to 20 digits)
256     *        if the number starts with '+', then set to international TOA
257     * @param onComplete
258     *        onComplete.obj will be an AsyncResult
259     *        ((AsyncResult)onComplete.obj).exception == null on success
260     *        ((AsyncResult)onComplete.obj).exception != null on fail
261     */
262    public void setMsisdnNumber(String alphaTag, String number,
263            Message onComplete) {
264
265        msisdn = number;
266        msisdnTag = alphaTag;
267
268        if(DBG) log("Set MSISDN: " + msisdnTag + " " + /*msisdn*/ "xxxxxxx");
269
270
271        AdnRecord adn = new AdnRecord(msisdnTag, msisdn);
272
273        new AdnRecordLoader(phone).updateEF(adn, EF_MSISDN, EF_EXT1, 1, null,
274                obtainMessage(EVENT_SET_MSISDN_DONE, onComplete));
275    }
276
277    public String getMsisdnAlphaTag() {
278        return msisdnTag;
279    }
280
281    public String getVoiceMailNumber() {
282        return voiceMailNum;
283    }
284
285    /**
286     * Set voice mail number to SIM record
287     *
288     * The voice mail number can be stored either in EF_MBDN (TS 51.011) or
289     * EF_MAILBOX_CPHS (CPHS 4.2)
290     *
291     * If EF_MBDN is available, store the voice mail number to EF_MBDN
292     *
293     * If EF_MAILBOX_CPHS is enabled, store the voice mail number to EF_CHPS
294     *
295     * So the voice mail number will be stored in both EFs if both are available
296     *
297     * Return error only if both EF_MBDN and EF_MAILBOX_CPHS fail.
298     *
299     * When the operation is complete, onComplete will be sent to its handler
300     *
301     * @param alphaTag alpha-tagging of the dailing nubmer (upto 10 characters)
302     * @param voiceNumber dailing nubmer (upto 20 digits)
303     *        if the number is start with '+', then set to international TOA
304     * @param onComplete
305     *        onComplete.obj will be an AsyncResult
306     *        ((AsyncResult)onComplete.obj).exception == null on success
307     *        ((AsyncResult)onComplete.obj).exception != null on fail
308     */
309    public void setVoiceMailNumber(String alphaTag, String voiceNumber,
310            Message onComplete) {
311        if (isVoiceMailFixed) {
312            AsyncResult.forMessage((onComplete)).exception =
313                    new IccVmFixedException("Voicemail number is fixed by operator");
314            onComplete.sendToTarget();
315            return;
316        }
317
318        newVoiceMailNum = voiceNumber;
319        newVoiceMailTag = alphaTag;
320
321        AdnRecord adn = new AdnRecord(newVoiceMailTag, newVoiceMailNum);
322
323        if (mailboxIndex != 0 && mailboxIndex != 0xff) {
324
325            new AdnRecordLoader(phone).updateEF(adn, EF_MBDN, EF_EXT6,
326                    mailboxIndex, null,
327                    obtainMessage(EVENT_SET_MBDN_DONE, onComplete));
328
329        } else if (isCphsMailboxEnabled()) {
330
331            new AdnRecordLoader(phone).updateEF(adn, EF_MAILBOX_CPHS,
332                    EF_EXT1, 1, null,
333                    obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE, onComplete));
334
335        } else {
336            AsyncResult.forMessage((onComplete)).exception =
337                    new IccVmNotSupportedException("Update SIM voice mailbox error");
338            onComplete.sendToTarget();
339        }
340    }
341
342    public String getVoiceMailAlphaTag()
343    {
344        return voiceMailTag;
345    }
346
347    /**
348     * Sets the SIM voice message waiting indicator records
349     * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported
350     * @param countWaiting The number of messages waiting, if known. Use
351     *                     -1 to indicate that an unknown number of
352     *                      messages are waiting
353     */
354    public void
355    setVoiceMessageWaiting(int line, int countWaiting) {
356        if (line != 1) {
357            // only profile 1 is supported
358            return;
359        }
360
361        // range check
362        if (countWaiting < 0) {
363            countWaiting = -1;
364        } else if (countWaiting > 0xff) {
365            // TS 23.040 9.2.3.24.2
366            // "The value 255 shall be taken to mean 255 or greater"
367            countWaiting = 0xff;
368        }
369
370        countVoiceMessages = countWaiting;
371
372        phone.notifyMessageWaitingIndicator();
373
374        try {
375            if (efMWIS != null) {
376                // TS 51.011 10.3.45
377
378                // lsb of byte 0 is 'voicemail' status
379                efMWIS[0] = (byte)((efMWIS[0] & 0xfe)
380                                    | (countVoiceMessages == 0 ? 0 : 1));
381
382                // byte 1 is the number of voice messages waiting
383                if (countWaiting < 0) {
384                    // The spec does not define what this should be
385                    // if we don't know the count
386                    efMWIS[1] = 0;
387                } else {
388                    efMWIS[1] = (byte) countWaiting;
389                }
390
391                phone.getIccFileHandler().updateEFLinearFixed(
392                    EF_MWIS, 1, efMWIS, null,
393                    obtainMessage (EVENT_UPDATE_DONE, EF_MWIS));
394            }
395
396            if (efCPHS_MWI != null) {
397                    // Refer CPHS4_2.WW6 B4.2.3
398                efCPHS_MWI[0] = (byte)((efCPHS_MWI[0] & 0xf0)
399                            | (countVoiceMessages == 0 ? 0x5 : 0xa));
400
401                phone.getIccFileHandler().updateEFTransparent(
402                    EF_VOICE_MAIL_INDICATOR_CPHS, efCPHS_MWI,
403                    obtainMessage (EVENT_UPDATE_DONE, EF_VOICE_MAIL_INDICATOR_CPHS));
404            }
405        } catch (ArrayIndexOutOfBoundsException ex) {
406            Log.w(LOG_TAG,
407                "Error saving voice mail state to SIM. Probably malformed SIM record", ex);
408        }
409    }
410
411    /**
412     * {@inheritDoc}
413     */
414    @Override
415    public boolean getVoiceCallForwardingFlag() {
416        return callForwardingEnabled;
417    }
418
419    /**
420     * {@inheritDoc}
421     */
422    @Override
423    public void setVoiceCallForwardingFlag(int line, boolean enable) {
424
425        if (line != 1) return; // only line 1 is supported
426
427        callForwardingEnabled = enable;
428
429        phone.notifyCallForwardingIndicator();
430
431        try {
432            if (mEfCfis != null) {
433                // lsb is of byte 1 is voice status
434                if (enable) {
435                    mEfCfis[1] |= 1;
436                } else {
437                    mEfCfis[1] &= 0xfe;
438                }
439
440                // TODO: Should really update other fields in EF_CFIS, eg,
441                // dialing number.  We don't read or use it right now.
442
443                phone.getIccFileHandler().updateEFLinearFixed(
444                        EF_CFIS, 1, mEfCfis, null,
445                        obtainMessage (EVENT_UPDATE_DONE, EF_CFIS));
446            }
447
448            if (mEfCff != null) {
449                if (enable) {
450                    mEfCff[0] = (byte) ((mEfCff[0] & CFF_LINE1_RESET)
451                            | CFF_UNCONDITIONAL_ACTIVE);
452                } else {
453                    mEfCff[0] = (byte) ((mEfCff[0] & CFF_LINE1_RESET)
454                            | CFF_UNCONDITIONAL_DEACTIVE);
455                }
456
457                phone.getIccFileHandler().updateEFTransparent(
458                        EF_CFF_CPHS, mEfCff,
459                        obtainMessage (EVENT_UPDATE_DONE, EF_CFF_CPHS));
460            }
461        } catch (ArrayIndexOutOfBoundsException ex) {
462            Log.w(LOG_TAG,
463                    "Error saving call fowarding flag to SIM. "
464                            + "Probably malformed SIM record", ex);
465
466        }
467    }
468
469    /**
470     * Called by STK Service when REFRESH is received.
471     * @param fileChanged indicates whether any files changed
472     * @param fileList if non-null, a list of EF files that changed
473     */
474    public void onRefresh(boolean fileChanged, int[] fileList) {
475        if (fileChanged) {
476            // A future optimization would be to inspect fileList and
477            // only reload those files that we care about.  For now,
478            // just re-fetch all SIM records that we cache.
479            fetchSimRecords();
480        }
481    }
482
483    /**
484     * {@inheritDoc}
485     */
486    @Override
487    public String getOperatorNumeric() {
488        if (imsi == null) {
489            Log.d(LOG_TAG, "getOperatorNumeric: IMSI == null");
490            return null;
491        }
492        if (mncLength == UNINITIALIZED || mncLength == UNKNOWN) {
493            Log.d(LOG_TAG, "getSIMOperatorNumeric: bad mncLength");
494            return null;
495        }
496
497        // STOPSHIP: to be removed
498        if (SystemProperties.getInt(com.android.internal.telephony.TelephonyProperties
499                .PROPERTY_NETWORK_LTE_ON_CDMA, 0) == 1) {
500            Log.e(LOG_TAG, "getOperatorNumeric: STOPSHIP bad numeric operators in lte");
501            return SystemProperties.get("ro.cdma.home.operator.numeric", "310004");
502        }
503        // Length = length of MCC + length of MNC
504        // length of mcc = 3 (TS 23.003 Section 2.2)
505        return imsi.substring(0, 3 + mncLength);
506    }
507
508    // ***** Overridden from Handler
509    public void handleMessage(Message msg) {
510        AsyncResult ar;
511        AdnRecord adn;
512
513        byte data[];
514
515        boolean isRecordLoadResponse = false;
516
517        try { switch (msg.what) {
518            case EVENT_SIM_READY:
519                onSimReady();
520            break;
521
522            case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
523                onRadioOffOrNotAvailable();
524            break;
525
526            /* IO events */
527            case EVENT_GET_IMSI_DONE:
528                isRecordLoadResponse = true;
529
530                ar = (AsyncResult)msg.obj;
531
532                if (ar.exception != null) {
533                    Log.e(LOG_TAG, "Exception querying IMSI, Exception:" + ar.exception);
534                    break;
535                }
536
537                imsi = (String) ar.result;
538
539                // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more
540                // than 15 (and usually 15).
541                if (imsi != null && (imsi.length() < 6 || imsi.length() > 15)) {
542                    Log.e(LOG_TAG, "invalid IMSI " + imsi);
543                    imsi = null;
544                }
545
546                Log.d(LOG_TAG, "IMSI: " + /* imsi.substring(0, 6) +*/ "xxxxxxx");
547
548                if (((mncLength == UNKNOWN) || (mncLength == 2)) &&
549                        ((imsi != null) && (imsi.length() >= 6))) {
550                    String mccmncCode = imsi.substring(0, 6);
551                    for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
552                        if (mccmnc.equals(mccmncCode)) {
553                            mncLength = 3;
554                            break;
555                        }
556                    }
557                }
558
559                if (mncLength == UNKNOWN) {
560                    // the SIM has told us all it knows, but it didn't know the mnc length.
561                    // guess using the mcc
562                    try {
563                        int mcc = Integer.parseInt(imsi.substring(0,3));
564                        mncLength = MccTable.smallestDigitsMccForMnc(mcc);
565                    } catch (NumberFormatException e) {
566                        mncLength = UNKNOWN;
567                        Log.e(LOG_TAG, "SIMRecords: Corrupt IMSI!");
568                    }
569                }
570
571                if (mncLength != UNKNOWN && mncLength != UNINITIALIZED) {
572                    // finally have both the imsi and the mncLength and can parse the imsi properly
573                    MccTable.updateMccMncConfiguration(phone, imsi.substring(0, 3 + mncLength));
574                }
575                phone.mIccCard.broadcastIccStateChangedIntent(
576                        SimCard.INTENT_VALUE_ICC_IMSI, null);
577            break;
578
579            case EVENT_GET_MBI_DONE:
580                boolean isValidMbdn;
581                isRecordLoadResponse = true;
582
583                ar = (AsyncResult)msg.obj;
584                data = (byte[]) ar.result;
585
586                isValidMbdn = false;
587                if (ar.exception == null) {
588                    // Refer TS 51.011 Section 10.3.44 for content details
589                    Log.d(LOG_TAG, "EF_MBI: " +
590                            IccUtils.bytesToHexString(data));
591
592                    // Voice mail record number stored first
593                    mailboxIndex = (int)data[0] & 0xff;
594
595                    // check if dailing numbe id valid
596                    if (mailboxIndex != 0 && mailboxIndex != 0xff) {
597                        Log.d(LOG_TAG, "Got valid mailbox number for MBDN");
598                        isValidMbdn = true;
599                    }
600                }
601
602                // one more record to load
603                recordsToLoad += 1;
604
605                if (isValidMbdn) {
606                    // Note: MBDN was not included in NUM_OF_SIM_RECORDS_LOADED
607                    new AdnRecordLoader(phone).loadFromEF(EF_MBDN, EF_EXT6,
608                            mailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE));
609                } else {
610                    // If this EF not present, try mailbox as in CPHS standard
611                    // CPHS (CPHS4_2.WW6) is a european standard.
612                    new AdnRecordLoader(phone).loadFromEF(EF_MAILBOX_CPHS,
613                            EF_EXT1, 1,
614                            obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
615                }
616
617                break;
618            case EVENT_GET_CPHS_MAILBOX_DONE:
619            case EVENT_GET_MBDN_DONE:
620                //Resetting the voice mail number and voice mail tag to null
621                //as these should be updated from the data read from EF_MBDN.
622                //If they are not reset, incase of invalid data/exception these
623                //variables are retaining their previous values and are
624                //causing invalid voice mailbox info display to user.
625                voiceMailNum = null;
626                voiceMailTag = null;
627                isRecordLoadResponse = true;
628
629                ar = (AsyncResult)msg.obj;
630
631                if (ar.exception != null) {
632
633                    Log.d(LOG_TAG, "Invalid or missing EF"
634                        + ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? "[MAILBOX]" : "[MBDN]"));
635
636                    // Bug #645770 fall back to CPHS
637                    // FIXME should use SST to decide
638
639                    if (msg.what == EVENT_GET_MBDN_DONE) {
640                        //load CPHS on fail...
641                        // FIXME right now, only load line1's CPHS voice mail entry
642
643                        recordsToLoad += 1;
644                        new AdnRecordLoader(phone).loadFromEF(
645                                EF_MAILBOX_CPHS, EF_EXT1, 1,
646                                obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
647                    }
648                    break;
649                }
650
651                adn = (AdnRecord)ar.result;
652
653                Log.d(LOG_TAG, "VM: " + adn +
654                        ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? " EF[MAILBOX]" : " EF[MBDN]"));
655
656                if (adn.isEmpty() && msg.what == EVENT_GET_MBDN_DONE) {
657                    // Bug #645770 fall back to CPHS
658                    // FIXME should use SST to decide
659                    // FIXME right now, only load line1's CPHS voice mail entry
660                    recordsToLoad += 1;
661                    new AdnRecordLoader(phone).loadFromEF(
662                            EF_MAILBOX_CPHS, EF_EXT1, 1,
663                            obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
664
665                    break;
666                }
667
668                voiceMailNum = adn.getNumber();
669                voiceMailTag = adn.getAlphaTag();
670            break;
671
672            case EVENT_GET_MSISDN_DONE:
673                isRecordLoadResponse = true;
674
675                ar = (AsyncResult)msg.obj;
676
677                if (ar.exception != null) {
678                    Log.d(LOG_TAG, "Invalid or missing EF[MSISDN]");
679                    break;
680                }
681
682                adn = (AdnRecord)ar.result;
683
684                msisdn = adn.getNumber();
685                msisdnTag = adn.getAlphaTag();
686
687                Log.d(LOG_TAG, "MSISDN: " + /*msisdn*/ "xxxxxxx");
688            break;
689
690            case EVENT_SET_MSISDN_DONE:
691                isRecordLoadResponse = false;
692                ar = (AsyncResult)msg.obj;
693
694                if (ar.userObj != null) {
695                    AsyncResult.forMessage(((Message) ar.userObj)).exception
696                            = ar.exception;
697                    ((Message) ar.userObj).sendToTarget();
698                }
699                break;
700
701            case EVENT_GET_MWIS_DONE:
702                isRecordLoadResponse = true;
703
704                ar = (AsyncResult)msg.obj;
705                data = (byte[])ar.result;
706
707                if (ar.exception != null) {
708                    break;
709                }
710
711                Log.d(LOG_TAG, "EF_MWIS: " +
712                   IccUtils.bytesToHexString(data));
713
714                efMWIS = data;
715
716                if ((data[0] & 0xff) == 0xff) {
717                    Log.d(LOG_TAG, "SIMRecords: Uninitialized record MWIS");
718                    break;
719                }
720
721                // Refer TS 51.011 Section 10.3.45 for the content description
722                boolean voiceMailWaiting = ((data[0] & 0x01) != 0);
723                countVoiceMessages = data[1] & 0xff;
724
725                if (voiceMailWaiting && countVoiceMessages == 0) {
726                    // Unknown count = -1
727                    countVoiceMessages = -1;
728                }
729
730                phone.notifyMessageWaitingIndicator();
731            break;
732
733            case EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE:
734                isRecordLoadResponse = true;
735
736                ar = (AsyncResult)msg.obj;
737                data = (byte[])ar.result;
738
739                if (ar.exception != null) {
740                    break;
741                }
742
743                efCPHS_MWI = data;
744
745                // Use this data if the EF[MWIS] exists and
746                // has been loaded
747
748                if (efMWIS == null) {
749                    int indicator = (int)(data[0] & 0xf);
750
751                    // Refer CPHS4_2.WW6 B4.2.3
752                    if (indicator == 0xA) {
753                        // Unknown count = -1
754                        countVoiceMessages = -1;
755                    } else if (indicator == 0x5) {
756                        countVoiceMessages = 0;
757                    }
758
759                    phone.notifyMessageWaitingIndicator();
760                }
761            break;
762
763            case EVENT_GET_ICCID_DONE:
764                isRecordLoadResponse = true;
765
766                ar = (AsyncResult)msg.obj;
767                data = (byte[])ar.result;
768
769                if (ar.exception != null) {
770                    break;
771                }
772
773                iccid = IccUtils.bcdToString(data, 0, data.length);
774
775                Log.d(LOG_TAG, "iccid: " + iccid);
776
777            break;
778
779
780            case EVENT_GET_AD_DONE:
781                try {
782                    isRecordLoadResponse = true;
783
784                    ar = (AsyncResult)msg.obj;
785                    data = (byte[])ar.result;
786
787                    if (ar.exception != null) {
788                        break;
789                    }
790
791                    Log.d(LOG_TAG, "EF_AD: " +
792                            IccUtils.bytesToHexString(data));
793
794                    if (data.length < 3) {
795                        Log.d(LOG_TAG, "SIMRecords: Corrupt AD data on SIM");
796                        break;
797                    }
798
799                    if (data.length == 3) {
800                        Log.d(LOG_TAG, "SIMRecords: MNC length not present in EF_AD");
801                        break;
802                    }
803
804                    mncLength = (int)data[3] & 0xf;
805
806                    if (mncLength == 0xf) {
807                        mncLength = UNKNOWN;
808                    }
809                } finally {
810                    if (((mncLength == UNINITIALIZED) || (mncLength == UNKNOWN) ||
811                            (mncLength == 2)) && ((imsi != null) && (imsi.length() >= 6))) {
812                        String mccmncCode = imsi.substring(0, 6);
813                        for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
814                            if (mccmnc.equals(mccmncCode)) {
815                                mncLength = 3;
816                                break;
817                            }
818                        }
819                    }
820
821                    if (mncLength == UNKNOWN || mncLength == UNINITIALIZED) {
822                        if (imsi != null) {
823                            try {
824                                int mcc = Integer.parseInt(imsi.substring(0,3));
825
826                                mncLength = MccTable.smallestDigitsMccForMnc(mcc);
827                            } catch (NumberFormatException e) {
828                                mncLength = UNKNOWN;
829                                Log.e(LOG_TAG, "SIMRecords: Corrupt IMSI!");
830                            }
831                        } else {
832                            // Indicate we got this info, but it didn't contain the length.
833                            mncLength = UNKNOWN;
834
835                            Log.d(LOG_TAG, "SIMRecords: MNC length not present in EF_AD");
836                        }
837                    }
838                    if (imsi != null && mncLength != UNKNOWN) {
839                        // finally have both imsi and the length of the mnc and can parse
840                        // the imsi properly
841                        MccTable.updateMccMncConfiguration(phone, imsi.substring(0, 3 + mncLength));
842                    }
843                }
844            break;
845
846            case EVENT_GET_SPN_DONE:
847                isRecordLoadResponse = true;
848                ar = (AsyncResult) msg.obj;
849                getSpnFsm(false, ar);
850            break;
851
852            case EVENT_GET_CFF_DONE:
853                isRecordLoadResponse = true;
854
855                ar = (AsyncResult) msg.obj;
856                data = (byte[]) ar.result;
857
858                if (ar.exception != null) {
859                    break;
860                }
861
862                Log.d(LOG_TAG, "EF_CFF_CPHS: " +
863                        IccUtils.bytesToHexString(data));
864                mEfCff = data;
865
866                if (mEfCfis == null) {
867                    callForwardingEnabled =
868                        ((data[0] & CFF_LINE1_MASK) == CFF_UNCONDITIONAL_ACTIVE);
869
870                    phone.notifyCallForwardingIndicator();
871                }
872                break;
873
874            case EVENT_GET_SPDI_DONE:
875                isRecordLoadResponse = true;
876
877                ar = (AsyncResult)msg.obj;
878                data = (byte[])ar.result;
879
880                if (ar.exception != null) {
881                    break;
882                }
883
884                parseEfSpdi(data);
885            break;
886
887            case EVENT_UPDATE_DONE:
888                ar = (AsyncResult)msg.obj;
889                if (ar.exception != null) {
890                    Log.i(LOG_TAG, "SIMRecords update failed", ar.exception);
891                }
892            break;
893
894            case EVENT_GET_PNN_DONE:
895                isRecordLoadResponse = true;
896
897                ar = (AsyncResult)msg.obj;
898                data = (byte[])ar.result;
899
900                if (ar.exception != null) {
901                    break;
902                }
903
904                SimTlv tlv = new SimTlv(data, 0, data.length);
905
906                for ( ; tlv.isValidObject() ; tlv.nextObject()) {
907                    if (tlv.getTag() == TAG_FULL_NETWORK_NAME) {
908                        pnnHomeName
909                            = IccUtils.networkNameToString(
910                                tlv.getData(), 0, tlv.getData().length);
911                        break;
912                    }
913                }
914            break;
915
916            case EVENT_GET_ALL_SMS_DONE:
917                isRecordLoadResponse = true;
918
919                ar = (AsyncResult)msg.obj;
920                if (ar.exception != null)
921                    break;
922
923                handleSmses((ArrayList) ar.result);
924                break;
925
926            case EVENT_MARK_SMS_READ_DONE:
927                Log.i("ENF", "marked read: sms " + msg.arg1);
928                break;
929
930
931            case EVENT_SMS_ON_SIM:
932                isRecordLoadResponse = false;
933
934                ar = (AsyncResult)msg.obj;
935
936                int[] index = (int[])ar.result;
937
938                if (ar.exception != null || index.length != 1) {
939                    Log.e(LOG_TAG, "[SIMRecords] Error on SMS_ON_SIM with exp "
940                            + ar.exception + " length " + index.length);
941                } else {
942                    Log.d(LOG_TAG, "READ EF_SMS RECORD index=" + index[0]);
943                    phone.getIccFileHandler().loadEFLinearFixed(EF_SMS,index[0],
944                            obtainMessage(EVENT_GET_SMS_DONE));
945                }
946                break;
947
948            case EVENT_GET_SMS_DONE:
949                isRecordLoadResponse = false;
950                ar = (AsyncResult)msg.obj;
951                if (ar.exception == null) {
952                    handleSms((byte[])ar.result);
953                } else {
954                    Log.e(LOG_TAG, "[SIMRecords] Error on GET_SMS with exp "
955                            + ar.exception);
956                }
957                break;
958            case EVENT_GET_SST_DONE:
959                isRecordLoadResponse = true;
960
961                ar = (AsyncResult)msg.obj;
962                data = (byte[])ar.result;
963
964                if (ar.exception != null) {
965                    break;
966                }
967
968                //Log.d(LOG_TAG, "SST: " + IccUtils.bytesToHexString(data));
969            break;
970
971            case EVENT_GET_INFO_CPHS_DONE:
972                isRecordLoadResponse = true;
973
974                ar = (AsyncResult)msg.obj;
975
976                if (ar.exception != null) {
977                    break;
978                }
979
980                mCphsInfo = (byte[])ar.result;
981
982                if (DBG) log("iCPHS: " + IccUtils.bytesToHexString(mCphsInfo));
983            break;
984
985            case EVENT_SET_MBDN_DONE:
986                isRecordLoadResponse = false;
987                ar = (AsyncResult)msg.obj;
988
989                if (ar.exception == null) {
990                    voiceMailNum = newVoiceMailNum;
991                    voiceMailTag = newVoiceMailTag;
992                }
993
994                if (isCphsMailboxEnabled()) {
995                    adn = new AdnRecord(voiceMailTag, voiceMailNum);
996                    Message onCphsCompleted = (Message) ar.userObj;
997
998                    /* write to cphs mailbox whenever it is available but
999                    * we only need notify caller once if both updating are
1000                    * successful.
1001                    *
1002                    * so if set_mbdn successful, notify caller here and set
1003                    * onCphsCompleted to null
1004                    */
1005                    if (ar.exception == null && ar.userObj != null) {
1006                        AsyncResult.forMessage(((Message) ar.userObj)).exception
1007                                = null;
1008                        ((Message) ar.userObj).sendToTarget();
1009
1010                        if (DBG) log("Callback with MBDN successful.");
1011
1012                        onCphsCompleted = null;
1013                    }
1014
1015                    new AdnRecordLoader(phone).
1016                            updateEF(adn, EF_MAILBOX_CPHS, EF_EXT1, 1, null,
1017                            obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE,
1018                                    onCphsCompleted));
1019                } else {
1020                    if (ar.userObj != null) {
1021                        AsyncResult.forMessage(((Message) ar.userObj)).exception
1022                                = ar.exception;
1023                        ((Message) ar.userObj).sendToTarget();
1024                    }
1025                }
1026                break;
1027            case EVENT_SET_CPHS_MAILBOX_DONE:
1028                isRecordLoadResponse = false;
1029                ar = (AsyncResult)msg.obj;
1030                if(ar.exception == null) {
1031                    voiceMailNum = newVoiceMailNum;
1032                    voiceMailTag = newVoiceMailTag;
1033                } else {
1034                    if (DBG) log("Set CPHS MailBox with exception: "
1035                            + ar.exception);
1036                }
1037                if (ar.userObj != null) {
1038                    if (DBG) log("Callback with CPHS MB successful.");
1039                    AsyncResult.forMessage(((Message) ar.userObj)).exception
1040                            = ar.exception;
1041                    ((Message) ar.userObj).sendToTarget();
1042                }
1043                break;
1044            case EVENT_SIM_REFRESH:
1045                isRecordLoadResponse = false;
1046                ar = (AsyncResult)msg.obj;
1047		if (DBG) log("Sim REFRESH with exception: " + ar.exception);
1048                if (ar.exception == null) {
1049                    handleSimRefresh((int[])(ar.result));
1050                }
1051                break;
1052            case EVENT_GET_CFIS_DONE:
1053                isRecordLoadResponse = true;
1054
1055                ar = (AsyncResult)msg.obj;
1056                data = (byte[])ar.result;
1057
1058                if (ar.exception != null) {
1059                    break;
1060                }
1061
1062                Log.d(LOG_TAG, "EF_CFIS: " +
1063                   IccUtils.bytesToHexString(data));
1064
1065                mEfCfis = data;
1066
1067                // Refer TS 51.011 Section 10.3.46 for the content description
1068                callForwardingEnabled = ((data[1] & 0x01) != 0);
1069
1070                phone.notifyCallForwardingIndicator();
1071                break;
1072
1073            case EVENT_GET_CSP_CPHS_DONE:
1074                isRecordLoadResponse = true;
1075
1076                ar = (AsyncResult)msg.obj;
1077
1078                if (ar.exception != null) {
1079                    Log.e(LOG_TAG,"Exception in fetching EF_CSP data " + ar.exception);
1080                    break;
1081                }
1082
1083                data = (byte[])ar.result;
1084
1085                Log.i(LOG_TAG,"EF_CSP: " + IccUtils.bytesToHexString(data));
1086                handleEfCspData(data);
1087                break;
1088
1089        }}catch (RuntimeException exc) {
1090            // I don't want these exceptions to be fatal
1091            Log.w(LOG_TAG, "Exception parsing SIM record", exc);
1092        } finally {
1093            // Count up record load responses even if they are fails
1094            if (isRecordLoadResponse) {
1095                onRecordLoaded();
1096            }
1097        }
1098    }
1099
1100    private void handleFileUpdate(int efid) {
1101        switch(efid) {
1102            case EF_MBDN:
1103                recordsToLoad++;
1104                new AdnRecordLoader(phone).loadFromEF(EF_MBDN, EF_EXT6,
1105                        mailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE));
1106                break;
1107            case EF_MAILBOX_CPHS:
1108                recordsToLoad++;
1109                new AdnRecordLoader(phone).loadFromEF(EF_MAILBOX_CPHS, EF_EXT1,
1110                        1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
1111                break;
1112            case EF_CSP_CPHS:
1113                recordsToLoad++;
1114                Log.i(LOG_TAG, "[CSP] SIM Refresh for EF_CSP_CPHS");
1115                phone.getIccFileHandler().loadEFTransparent(EF_CSP_CPHS,
1116                        obtainMessage(EVENT_GET_CSP_CPHS_DONE));
1117                break;
1118            default:
1119                // For now, fetch all records if this is not a
1120                // voicemail number.
1121                // TODO: Handle other cases, instead of fetching all.
1122                adnCache.reset();
1123                fetchSimRecords();
1124                break;
1125        }
1126    }
1127
1128    private void handleSimRefresh(int[] result) {
1129        if (result == null || result.length == 0) {
1130	    if (DBG) log("handleSimRefresh without input");
1131            return;
1132        }
1133
1134        switch ((result[0])) {
1135            case CommandsInterface.SIM_REFRESH_FILE_UPDATED:
1136 		if (DBG) log("handleSimRefresh with SIM_REFRESH_FILE_UPDATED");
1137                // result[1] contains the EFID of the updated file.
1138                int efid = result[1];
1139                handleFileUpdate(efid);
1140                break;
1141            case CommandsInterface.SIM_REFRESH_INIT:
1142		if (DBG) log("handleSimRefresh with SIM_REFRESH_INIT");
1143                // need to reload all files (that we care about)
1144                adnCache.reset();
1145                fetchSimRecords();
1146                break;
1147            case CommandsInterface.SIM_REFRESH_RESET:
1148		if (DBG) log("handleSimRefresh with SIM_REFRESH_RESET");
1149                phone.mCM.setRadioPower(false, null);
1150                /* Note: no need to call setRadioPower(true).  Assuming the desired
1151                * radio power state is still ON (as tracked by ServiceStateTracker),
1152                * ServiceStateTracker will call setRadioPower when it receives the
1153                * RADIO_STATE_CHANGED notification for the power off.  And if the
1154                * desired power state has changed in the interim, we don't want to
1155                * override it with an unconditional power on.
1156                */
1157                break;
1158            default:
1159                // unknown refresh operation
1160		if (DBG) log("handleSimRefresh with unknown operation");
1161                break;
1162        }
1163    }
1164
1165    private void handleSms(byte[] ba) {
1166        if (ba[0] != 0)
1167            Log.d("ENF", "status : " + ba[0]);
1168
1169        // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1170        // 3 == "received by MS from network; message to be read"
1171        if (ba[0] == 3) {
1172            int n = ba.length;
1173
1174            // Note: Data may include trailing FF's.  That's OK; message
1175            // should still parse correctly.
1176            byte[] pdu = new byte[n - 1];
1177            System.arraycopy(ba, 1, pdu, 0, n - 1);
1178            SmsMessage message = SmsMessage.createFromPdu(pdu);
1179
1180            phone.mSMS.dispatchMessage(message);
1181        }
1182    }
1183
1184
1185    private void handleSmses(ArrayList messages) {
1186        int count = messages.size();
1187
1188        for (int i = 0; i < count; i++) {
1189            byte[] ba = (byte[]) messages.get(i);
1190
1191            if (ba[0] != 0)
1192                Log.i("ENF", "status " + i + ": " + ba[0]);
1193
1194            // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1195            // 3 == "received by MS from network; message to be read"
1196
1197            if (ba[0] == 3) {
1198                int n = ba.length;
1199
1200                // Note: Data may include trailing FF's.  That's OK; message
1201                // should still parse correctly.
1202                byte[] pdu = new byte[n - 1];
1203                System.arraycopy(ba, 1, pdu, 0, n - 1);
1204                SmsMessage message = SmsMessage.createFromPdu(pdu);
1205
1206                phone.mSMS.dispatchMessage(message);
1207
1208                // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1209                // 1 == "received by MS from network; message read"
1210
1211                ba[0] = 1;
1212
1213                if (false) { // XXX writing seems to crash RdoServD
1214                    phone.getIccFileHandler().updateEFLinearFixed(EF_SMS,
1215                            i, ba, null, obtainMessage(EVENT_MARK_SMS_READ_DONE, i));
1216                }
1217            }
1218        }
1219    }
1220
1221    protected void onRecordLoaded() {
1222        // One record loaded successfully or failed, In either case
1223        // we need to update the recordsToLoad count
1224        recordsToLoad -= 1;
1225
1226        if (recordsToLoad == 0 && recordsRequested == true) {
1227            onAllRecordsLoaded();
1228        } else if (recordsToLoad < 0) {
1229            Log.e(LOG_TAG, "SIMRecords: recordsToLoad <0, programmer error suspected");
1230            recordsToLoad = 0;
1231        }
1232    }
1233
1234    protected void onAllRecordsLoaded() {
1235        Log.d(LOG_TAG, "SIMRecords: record load complete");
1236
1237        String operator = getOperatorNumeric();
1238
1239        // Some fields require more than one SIM record to set
1240
1241        phone.setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, operator);
1242
1243        if (imsi != null) {
1244            phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
1245                    MccTable.countryCodeForMcc(Integer.parseInt(imsi.substring(0,3))));
1246        }
1247        else {
1248            Log.e("SIM", "[SIMRecords] onAllRecordsLoaded: imsi is NULL!");
1249        }
1250
1251        setVoiceMailByCountry(operator);
1252        setSpnFromConfig(operator);
1253
1254        recordsLoadedRegistrants.notifyRegistrants(
1255            new AsyncResult(null, null, null));
1256        phone.mIccCard.broadcastIccStateChangedIntent(
1257                SimCard.INTENT_VALUE_ICC_LOADED, null);
1258    }
1259
1260    //***** Private methods
1261
1262    private void setSpnFromConfig(String carrier) {
1263        if (mSpnOverride.containsCarrier(carrier)) {
1264            spn = mSpnOverride.getSpn(carrier);
1265        }
1266    }
1267
1268
1269    private void setVoiceMailByCountry (String spn) {
1270        if (mVmConfig.containsCarrier(spn)) {
1271            isVoiceMailFixed = true;
1272            voiceMailNum = mVmConfig.getVoiceMailNumber(spn);
1273            voiceMailTag = mVmConfig.getVoiceMailTag(spn);
1274        }
1275    }
1276
1277    public void onSimReady() {
1278        /* broadcast intent SIM_READY here so that we can make sure
1279          READY is sent before IMSI ready
1280        */
1281        phone.mIccCard.broadcastIccStateChangedIntent(
1282                SimCard.INTENT_VALUE_ICC_READY, null);
1283
1284        fetchSimRecords();
1285    }
1286
1287    private void fetchSimRecords() {
1288        recordsRequested = true;
1289        IccFileHandler iccFh = phone.getIccFileHandler();
1290
1291        Log.v(LOG_TAG, "SIMRecords:fetchSimRecords " + recordsToLoad);
1292
1293        phone.mCM.getIMSI(obtainMessage(EVENT_GET_IMSI_DONE));
1294        recordsToLoad++;
1295
1296        iccFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
1297        recordsToLoad++;
1298
1299        // FIXME should examine EF[MSISDN]'s capability configuration
1300        // to determine which is the voice/data/fax line
1301        new AdnRecordLoader(phone).loadFromEF(EF_MSISDN, EF_EXT1, 1,
1302                    obtainMessage(EVENT_GET_MSISDN_DONE));
1303        recordsToLoad++;
1304
1305        // Record number is subscriber profile
1306        iccFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE));
1307        recordsToLoad++;
1308
1309        iccFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE));
1310        recordsToLoad++;
1311
1312        // Record number is subscriber profile
1313        iccFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE));
1314        recordsToLoad++;
1315
1316
1317        // Also load CPHS-style voice mail indicator, which stores
1318        // the same info as EF[MWIS]. If both exist, both are updated
1319        // but the EF[MWIS] data is preferred
1320        // Please note this must be loaded after EF[MWIS]
1321        iccFh.loadEFTransparent(
1322                EF_VOICE_MAIL_INDICATOR_CPHS,
1323                obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE));
1324        recordsToLoad++;
1325
1326        // Same goes for Call Forward Status indicator: fetch both
1327        // EF[CFIS] and CPHS-EF, with EF[CFIS] preferred.
1328        iccFh.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE));
1329        recordsToLoad++;
1330        iccFh.loadEFTransparent(EF_CFF_CPHS, obtainMessage(EVENT_GET_CFF_DONE));
1331        recordsToLoad++;
1332
1333
1334        getSpnFsm(true, null);
1335
1336        iccFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE));
1337        recordsToLoad++;
1338
1339        iccFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE));
1340        recordsToLoad++;
1341
1342        iccFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE));
1343        recordsToLoad++;
1344
1345        iccFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE));
1346        recordsToLoad++;
1347
1348        iccFh.loadEFTransparent(EF_CSP_CPHS,obtainMessage(EVENT_GET_CSP_CPHS_DONE));
1349        recordsToLoad++;
1350
1351        // XXX should seek instead of examining them all
1352        if (false) { // XXX
1353            iccFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE));
1354            recordsToLoad++;
1355        }
1356
1357        if (CRASH_RIL) {
1358            String sms = "0107912160130310f20404d0110041007030208054832b0120"
1359                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1360                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1361                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1362                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1363                         + "ffffffffffffffffffffffffffffff";
1364            byte[] ba = IccUtils.hexStringToBytes(sms);
1365
1366            iccFh.updateEFLinearFixed(EF_SMS, 1, ba, null,
1367                            obtainMessage(EVENT_MARK_SMS_READ_DONE, 1));
1368        }
1369    }
1370
1371    /**
1372     * Returns the SpnDisplayRule based on settings on the SIM and the
1373     * specified plmn (currently-registered PLMN).  See TS 22.101 Annex A
1374     * and TS 51.011 10.3.11 for details.
1375     *
1376     * If the SPN is not found on the SIM, the rule is always PLMN_ONLY.
1377     */
1378    @Override
1379    public int getDisplayRule(String plmn) {
1380        int rule;
1381        if (spn == null || spnDisplayCondition == -1) {
1382            // EF_SPN was not found on the SIM, or not yet loaded.  Just show ONS.
1383            rule = SPN_RULE_SHOW_PLMN;
1384        } else if (isOnMatchingPlmn(plmn)) {
1385            rule = SPN_RULE_SHOW_SPN;
1386            if ((spnDisplayCondition & 0x01) == 0x01) {
1387                // ONS required when registered to HPLMN or PLMN in EF_SPDI
1388                rule |= SPN_RULE_SHOW_PLMN;
1389            }
1390        } else {
1391            rule = SPN_RULE_SHOW_PLMN;
1392            if ((spnDisplayCondition & 0x02) == 0x00) {
1393                // SPN required if not registered to HPLMN or PLMN in EF_SPDI
1394                rule |= SPN_RULE_SHOW_SPN;
1395            }
1396        }
1397        return rule;
1398    }
1399
1400    /**
1401     * Checks if plmn is HPLMN or on the spdiNetworks list.
1402     */
1403    private boolean isOnMatchingPlmn(String plmn) {
1404        if (plmn == null) return false;
1405
1406        if (plmn.equals(getOperatorNumeric())) {
1407            return true;
1408        }
1409
1410        if (spdiNetworks != null) {
1411            for (String spdiNet : spdiNetworks) {
1412                if (plmn.equals(spdiNet)) {
1413                    return true;
1414                }
1415            }
1416        }
1417        return false;
1418    }
1419
1420    /**
1421     * States of Get SPN Finite State Machine which only used by getSpnFsm()
1422     */
1423    private enum Get_Spn_Fsm_State {
1424        IDLE,               // No initialized
1425        INIT,               // Start FSM
1426        READ_SPN_3GPP,      // Load EF_SPN firstly
1427        READ_SPN_CPHS,      // Load EF_SPN_CPHS secondly
1428        READ_SPN_SHORT_CPHS // Load EF_SPN_SHORT_CPHS last
1429    }
1430
1431    /**
1432     * Finite State Machine to load Service Provider Name , which can be stored
1433     * in either EF_SPN (3GPP), EF_SPN_CPHS, or EF_SPN_SHORT_CPHS (CPHS4.2)
1434     *
1435     * After starting, FSM will search SPN EFs in order and stop after finding
1436     * the first valid SPN
1437     *
1438     * @param start set true only for initialize loading
1439     * @param ar the AsyncResult from loadEFTransparent
1440     *        ar.exception holds exception in error
1441     *        ar.result is byte[] for data in success
1442     */
1443    private void getSpnFsm(boolean start, AsyncResult ar) {
1444        byte[] data;
1445
1446        if (start) {
1447            spnState = Get_Spn_Fsm_State.INIT;
1448        }
1449
1450        switch(spnState){
1451            case INIT:
1452                spn = null;
1453
1454                phone.getIccFileHandler().loadEFTransparent( EF_SPN,
1455                        obtainMessage(EVENT_GET_SPN_DONE));
1456                recordsToLoad++;
1457
1458                spnState = Get_Spn_Fsm_State.READ_SPN_3GPP;
1459                break;
1460            case READ_SPN_3GPP:
1461                if (ar != null && ar.exception == null) {
1462                    data = (byte[]) ar.result;
1463                    spnDisplayCondition = 0xff & data[0];
1464                    spn = IccUtils.adnStringFieldToString(data, 1, data.length - 1);
1465
1466                    if (DBG) log("Load EF_SPN: " + spn
1467                            + " spnDisplayCondition: " + spnDisplayCondition);
1468                    phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
1469
1470                    spnState = Get_Spn_Fsm_State.IDLE;
1471                } else {
1472                    phone.getIccFileHandler().loadEFTransparent( EF_SPN_CPHS,
1473                            obtainMessage(EVENT_GET_SPN_DONE));
1474                    recordsToLoad++;
1475
1476                    spnState = Get_Spn_Fsm_State.READ_SPN_CPHS;
1477
1478                    // See TS 51.011 10.3.11.  Basically, default to
1479                    // show PLMN always, and SPN also if roaming.
1480                    spnDisplayCondition = -1;
1481                }
1482                break;
1483            case READ_SPN_CPHS:
1484                if (ar != null && ar.exception == null) {
1485                    data = (byte[]) ar.result;
1486                    spn = IccUtils.adnStringFieldToString(
1487                            data, 0, data.length - 1 );
1488
1489                    if (DBG) log("Load EF_SPN_CPHS: " + spn);
1490                    phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
1491
1492                    spnState = Get_Spn_Fsm_State.IDLE;
1493                } else {
1494                    phone.getIccFileHandler().loadEFTransparent(
1495                            EF_SPN_SHORT_CPHS, obtainMessage(EVENT_GET_SPN_DONE));
1496                    recordsToLoad++;
1497
1498                    spnState = Get_Spn_Fsm_State.READ_SPN_SHORT_CPHS;
1499                }
1500                break;
1501            case READ_SPN_SHORT_CPHS:
1502                if (ar != null && ar.exception == null) {
1503                    data = (byte[]) ar.result;
1504                    spn = IccUtils.adnStringFieldToString(
1505                            data, 0, data.length - 1);
1506
1507                    if (DBG) log("Load EF_SPN_SHORT_CPHS: " + spn);
1508                    phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
1509                }else {
1510                    if (DBG) log("No SPN loaded in either CHPS or 3GPP");
1511                }
1512
1513                spnState = Get_Spn_Fsm_State.IDLE;
1514                break;
1515            default:
1516                spnState = Get_Spn_Fsm_State.IDLE;
1517        }
1518    }
1519
1520    /**
1521     * Parse TS 51.011 EF[SPDI] record
1522     * This record contains the list of numeric network IDs that
1523     * are treated specially when determining SPN display
1524     */
1525    private void
1526    parseEfSpdi(byte[] data) {
1527        SimTlv tlv = new SimTlv(data, 0, data.length);
1528
1529        byte[] plmnEntries = null;
1530
1531        for ( ; tlv.isValidObject() ; tlv.nextObject()) {
1532            // Skip SPDI tag, if existant
1533            if (tlv.getTag() == TAG_SPDI) {
1534              tlv = new SimTlv(tlv.getData(), 0, tlv.getData().length);
1535            }
1536            // There should only be one TAG_SPDI_PLMN_LIST
1537            if (tlv.getTag() == TAG_SPDI_PLMN_LIST) {
1538                plmnEntries = tlv.getData();
1539                break;
1540            }
1541        }
1542
1543        if (plmnEntries == null) {
1544            return;
1545        }
1546
1547        spdiNetworks = new ArrayList<String>(plmnEntries.length / 3);
1548
1549        for (int i = 0 ; i + 2 < plmnEntries.length ; i += 3) {
1550            String plmnCode;
1551            plmnCode = IccUtils.bcdToString(plmnEntries, i, 3);
1552
1553            // Valid operator codes are 5 or 6 digits
1554            if (plmnCode.length() >= 5) {
1555                log("EF_SPDI network: " + plmnCode);
1556                spdiNetworks.add(plmnCode);
1557            }
1558        }
1559    }
1560
1561    /**
1562     * check to see if Mailbox Number is allocated and activated in CPHS SST
1563     */
1564    private boolean isCphsMailboxEnabled() {
1565        if (mCphsInfo == null)  return false;
1566        return ((mCphsInfo[1] & CPHS_SST_MBN_MASK) == CPHS_SST_MBN_ENABLED );
1567    }
1568
1569    protected void log(String s) {
1570        Log.d(LOG_TAG, "[SIMRecords] " + s);
1571    }
1572
1573    /**
1574     * Return true if "Restriction of menu options for manual PLMN selection"
1575     * bit is set or EF_CSP data is unavailable, return false otherwise.
1576     */
1577    public boolean isCspPlmnEnabled() {
1578        return mCspPlmnEnabled;
1579    }
1580
1581    /**
1582     * Parse EF_CSP data and check if
1583     * "Restriction of menu options for manual PLMN selection" is
1584     * Enabled/Disabled
1585     *
1586     * @param data EF_CSP hex data.
1587     */
1588    private void handleEfCspData(byte[] data) {
1589        // As per spec CPHS4_2.WW6, CPHS B.4.7.1, EF_CSP contains CPHS defined
1590        // 18 bytes (i.e 9 service groups info) and additional data specific to
1591        // operator. The valueAddedServicesGroup is not part of standard
1592        // services. This is operator specific and can be programmed any where.
1593        // Normally this is programmed as 10th service after the standard
1594        // services.
1595        int usedCspGroups = data.length / 2;
1596        // This is the "Servive Group Number" of "Value Added Services Group".
1597        byte valueAddedServicesGroup = (byte)0xC0;
1598
1599        mCspPlmnEnabled = true;
1600        for (int i = 0; i < usedCspGroups; i++) {
1601             if (data[2 * i] == valueAddedServicesGroup) {
1602                 Log.i(LOG_TAG, "[CSP] found ValueAddedServicesGroup, value "
1603                       + data[(2 * i) + 1]);
1604                 if ((data[(2 * i) + 1] & 0x80) == 0x80) {
1605                     // Bit 8 is for
1606                     // "Restriction of menu options for manual PLMN selection".
1607                     // Operator Selection menu should be enabled.
1608                     mCspPlmnEnabled = true;
1609                 } else {
1610                     mCspPlmnEnabled = false;
1611                     // Operator Selection menu should be disabled.
1612                     // Operator Selection Mode should be set to Automatic.
1613                     Log.i(LOG_TAG,"[CSP] Set Automatic Network Selection");
1614                     phone.setNetworkSelectionModeAutomatic(null);
1615                 }
1616                 return;
1617             }
1618        }
1619
1620        Log.w(LOG_TAG, "[CSP] Value Added Service Group (0xC0), not found!");
1621    }
1622}
1623