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