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