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