SIMRecords.java revision 3d59f8b41874e5e1183e236b7b079a0a0c510f6c
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                //Resetting the voice mail number and voice mail tag to null
564                //as these should be updated from the data read from EF_MBDN.
565                //If they are not reset, incase of invalid data/exception these
566                //variables are retaining their previous values and are
567                //causing invalid voice mailbox info display to user.
568                voiceMailNum = null;
569                voiceMailTag = null;
570                isRecordLoadResponse = true;
571
572                ar = (AsyncResult)msg.obj;
573
574                if (ar.exception != null) {
575
576                    Log.d(LOG_TAG, "Invalid or missing EF"
577                        + ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? "[MAILBOX]" : "[MBDN]"));
578
579                    // Bug #645770 fall back to CPHS
580                    // FIXME should use SST to decide
581
582                    if (msg.what == EVENT_GET_MBDN_DONE) {
583                        //load CPHS on fail...
584                        // FIXME right now, only load line1's CPHS voice mail entry
585
586                        recordsToLoad += 1;
587                        new AdnRecordLoader(phone).loadFromEF(
588                                EF_MAILBOX_CPHS, EF_EXT1, 1,
589                                obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
590                    }
591                    break;
592                }
593
594                adn = (AdnRecord)ar.result;
595
596                Log.d(LOG_TAG, "VM: " + adn +
597                        ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? " EF[MAILBOX]" : " EF[MBDN]"));
598
599                if (adn.isEmpty() && msg.what == EVENT_GET_MBDN_DONE) {
600                    // Bug #645770 fall back to CPHS
601                    // FIXME should use SST to decide
602                    // FIXME right now, only load line1's CPHS voice mail entry
603                    recordsToLoad += 1;
604                    new AdnRecordLoader(phone).loadFromEF(
605                            EF_MAILBOX_CPHS, EF_EXT1, 1,
606                            obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
607
608                    break;
609                }
610
611                voiceMailNum = adn.getNumber();
612                voiceMailTag = adn.getAlphaTag();
613            break;
614
615            case EVENT_GET_MSISDN_DONE:
616                isRecordLoadResponse = true;
617
618                ar = (AsyncResult)msg.obj;
619
620                if (ar.exception != null) {
621                    Log.d(LOG_TAG, "Invalid or missing EF[MSISDN]");
622                    break;
623                }
624
625                adn = (AdnRecord)ar.result;
626
627                msisdn = adn.getNumber();
628                msisdnTag = adn.getAlphaTag();
629
630                Log.d(LOG_TAG, "MSISDN: " + msisdn);
631            break;
632
633            case EVENT_SET_MSISDN_DONE:
634                isRecordLoadResponse = false;
635                ar = (AsyncResult)msg.obj;
636
637                if (ar.userObj != null) {
638                    AsyncResult.forMessage(((Message) ar.userObj)).exception
639                            = ar.exception;
640                    ((Message) ar.userObj).sendToTarget();
641                }
642                break;
643
644            case EVENT_GET_MWIS_DONE:
645                isRecordLoadResponse = true;
646
647                ar = (AsyncResult)msg.obj;
648                data = (byte[])ar.result;
649
650                if (ar.exception != null) {
651                    break;
652                }
653
654                Log.d(LOG_TAG, "EF_MWIS: " +
655                   IccUtils.bytesToHexString(data));
656
657                efMWIS = data;
658
659                if ((data[0] & 0xff) == 0xff) {
660                    Log.d(LOG_TAG, "SIMRecords: Uninitialized record MWIS");
661                    break;
662                }
663
664                // Refer TS 51.011 Section 10.3.45 for the content description
665                boolean voiceMailWaiting = ((data[0] & 0x01) != 0);
666                countVoiceMessages = data[1] & 0xff;
667
668                if (voiceMailWaiting && countVoiceMessages == 0) {
669                    // Unknown count = -1
670                    countVoiceMessages = -1;
671                }
672
673                ((GSMPhone) phone).notifyMessageWaitingIndicator();
674            break;
675
676            case EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE:
677                isRecordLoadResponse = true;
678
679                ar = (AsyncResult)msg.obj;
680                data = (byte[])ar.result;
681
682                if (ar.exception != null) {
683                    break;
684                }
685
686                efCPHS_MWI = data;
687
688                // Use this data if the EF[MWIS] exists and
689                // has been loaded
690
691                if (efMWIS == null) {
692                    int indicator = (int)(data[0] & 0xf);
693
694                    // Refer CPHS4_2.WW6 B4.2.3
695                    if (indicator == 0xA) {
696                        // Unknown count = -1
697                        countVoiceMessages = -1;
698                    } else if (indicator == 0x5) {
699                        countVoiceMessages = 0;
700                    }
701
702                    ((GSMPhone) phone).notifyMessageWaitingIndicator();
703                }
704            break;
705
706            case EVENT_GET_ICCID_DONE:
707                isRecordLoadResponse = true;
708
709                ar = (AsyncResult)msg.obj;
710                data = (byte[])ar.result;
711
712                if (ar.exception != null) {
713                    break;
714                }
715
716                iccid = IccUtils.bcdToString(data, 0, data.length);
717
718                Log.d(LOG_TAG, "iccid: " + iccid);
719
720            break;
721
722
723            case EVENT_GET_AD_DONE:
724                try {
725                    isRecordLoadResponse = true;
726
727                    ar = (AsyncResult)msg.obj;
728                    data = (byte[])ar.result;
729
730                    if (ar.exception != null) {
731                        break;
732                    }
733
734                    Log.d(LOG_TAG, "EF_AD: " +
735                            IccUtils.bytesToHexString(data));
736
737                    if (data.length < 3) {
738                        Log.d(LOG_TAG, "SIMRecords: Corrupt AD data on SIM");
739                        break;
740                    }
741
742                    if (data.length == 3) {
743                        Log.d(LOG_TAG, "SIMRecords: MNC length not present in EF_AD");
744                        break;
745                    }
746
747                    mncLength = (int)data[3] & 0xf;
748
749                    if (mncLength == 0xf) {
750                        mncLength = UNKNOWN;
751                    }
752                } finally {
753                    if (mncLength == UNKNOWN || mncLength == UNINITIALIZED) {
754                        if (imsi != null) {
755                            try {
756                                int mcc = Integer.parseInt(imsi.substring(0,3));
757
758                                mncLength = MccTable.smallestDigitsMccForMnc(mcc);
759                            } catch (NumberFormatException e) {
760                                mncLength = UNKNOWN;
761                                Log.e(LOG_TAG, "SIMRecords: Corrupt IMSI!");
762                            }
763                        } else {
764                            // Indicate we got this info, but it didn't contain the length.
765                            mncLength = UNKNOWN;
766
767                            Log.d(LOG_TAG, "SIMRecords: MNC length not present in EF_AD");
768                        }
769                    }
770                    if (imsi != null && mncLength != UNKNOWN) {
771                        // finally have both imsi and the length of the mnc and can parse
772                        // the imsi properly
773                        MccTable.updateMccMncConfiguration(phone, imsi.substring(0, 3 + mncLength));
774                    }
775                }
776            break;
777
778            case EVENT_GET_SPN_DONE:
779                isRecordLoadResponse = true;
780                ar = (AsyncResult) msg.obj;
781                getSpnFsm(false, ar);
782            break;
783
784            case EVENT_GET_CFF_DONE:
785                isRecordLoadResponse = true;
786
787                ar = (AsyncResult) msg.obj;
788                data = (byte[]) ar.result;
789
790                if (ar.exception != null) {
791                    break;
792                }
793
794                Log.d(LOG_TAG, "EF_CFF_CPHS: " +
795                        IccUtils.bytesToHexString(data));
796                mEfCff = data;
797
798                if (mEfCfis == null) {
799                    callForwardingEnabled =
800                        ((data[0] & CFF_LINE1_MASK) == CFF_UNCONDITIONAL_ACTIVE);
801
802                    ((GSMPhone) phone).notifyCallForwardingIndicator();
803                }
804                break;
805
806            case EVENT_GET_SPDI_DONE:
807                isRecordLoadResponse = true;
808
809                ar = (AsyncResult)msg.obj;
810                data = (byte[])ar.result;
811
812                if (ar.exception != null) {
813                    break;
814                }
815
816                parseEfSpdi(data);
817            break;
818
819            case EVENT_UPDATE_DONE:
820                ar = (AsyncResult)msg.obj;
821                if (ar.exception != null) {
822                    Log.i(LOG_TAG, "SIMRecords update failed", ar.exception);
823                }
824            break;
825
826            case EVENT_GET_PNN_DONE:
827                isRecordLoadResponse = true;
828
829                ar = (AsyncResult)msg.obj;
830                data = (byte[])ar.result;
831
832                if (ar.exception != null) {
833                    break;
834                }
835
836                SimTlv tlv = new SimTlv(data, 0, data.length);
837
838                for ( ; tlv.isValidObject() ; tlv.nextObject()) {
839                    if (tlv.getTag() == TAG_FULL_NETWORK_NAME) {
840                        pnnHomeName
841                            = IccUtils.networkNameToString(
842                                tlv.getData(), 0, tlv.getData().length);
843                        break;
844                    }
845                }
846            break;
847
848            case EVENT_GET_ALL_SMS_DONE:
849                isRecordLoadResponse = true;
850
851                ar = (AsyncResult)msg.obj;
852                if (ar.exception != null)
853                    break;
854
855                handleSmses((ArrayList) ar.result);
856                break;
857
858            case EVENT_MARK_SMS_READ_DONE:
859                Log.i("ENF", "marked read: sms " + msg.arg1);
860                break;
861
862
863            case EVENT_SMS_ON_SIM:
864                isRecordLoadResponse = false;
865
866                ar = (AsyncResult)msg.obj;
867
868                int[] index = (int[])ar.result;
869
870                if (ar.exception != null || index.length != 1) {
871                    Log.e(LOG_TAG, "[SIMRecords] Error on SMS_ON_SIM with exp "
872                            + ar.exception + " length " + index.length);
873                } else {
874                    Log.d(LOG_TAG, "READ EF_SMS RECORD index=" + index[0]);
875                    phone.getIccFileHandler().loadEFLinearFixed(EF_SMS,index[0],
876                            obtainMessage(EVENT_GET_SMS_DONE));
877                }
878                break;
879
880            case EVENT_GET_SMS_DONE:
881                isRecordLoadResponse = false;
882                ar = (AsyncResult)msg.obj;
883                if (ar.exception == null) {
884                    handleSms((byte[])ar.result);
885                } else {
886                    Log.e(LOG_TAG, "[SIMRecords] Error on GET_SMS with exp "
887                            + ar.exception);
888                }
889                break;
890            case EVENT_GET_SST_DONE:
891                isRecordLoadResponse = true;
892
893                ar = (AsyncResult)msg.obj;
894                data = (byte[])ar.result;
895
896                if (ar.exception != null) {
897                    break;
898                }
899
900                //Log.d(LOG_TAG, "SST: " + IccUtils.bytesToHexString(data));
901            break;
902
903            case EVENT_GET_INFO_CPHS_DONE:
904                isRecordLoadResponse = true;
905
906                ar = (AsyncResult)msg.obj;
907
908                if (ar.exception != null) {
909                    break;
910                }
911
912                mCphsInfo = (byte[])ar.result;
913
914                if (DBG) log("iCPHS: " + IccUtils.bytesToHexString(mCphsInfo));
915            break;
916
917            case EVENT_SET_MBDN_DONE:
918                isRecordLoadResponse = false;
919                ar = (AsyncResult)msg.obj;
920
921                if (ar.exception == null) {
922                    voiceMailNum = newVoiceMailNum;
923                    voiceMailTag = newVoiceMailTag;
924                }
925
926                if (isCphsMailboxEnabled()) {
927                    adn = new AdnRecord(voiceMailTag, voiceMailNum);
928                    Message onCphsCompleted = (Message) ar.userObj;
929
930                    /* write to cphs mailbox whenever it is available but
931                    * we only need notify caller once if both updating are
932                    * successful.
933                    *
934                    * so if set_mbdn successful, notify caller here and set
935                    * onCphsCompleted to null
936                    */
937                    if (ar.exception == null && ar.userObj != null) {
938                        AsyncResult.forMessage(((Message) ar.userObj)).exception
939                                = null;
940                        ((Message) ar.userObj).sendToTarget();
941
942                        if (DBG) log("Callback with MBDN successful.");
943
944                        onCphsCompleted = null;
945                    }
946
947                    new AdnRecordLoader(phone).
948                            updateEF(adn, EF_MAILBOX_CPHS, EF_EXT1, 1, null,
949                            obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE,
950                                    onCphsCompleted));
951                } else {
952                    if (ar.userObj != null) {
953                        AsyncResult.forMessage(((Message) ar.userObj)).exception
954                                = ar.exception;
955                        ((Message) ar.userObj).sendToTarget();
956                    }
957                }
958                break;
959            case EVENT_SET_CPHS_MAILBOX_DONE:
960                isRecordLoadResponse = false;
961                ar = (AsyncResult)msg.obj;
962                if(ar.exception == null) {
963                    voiceMailNum = newVoiceMailNum;
964                    voiceMailTag = newVoiceMailTag;
965                } else {
966                    if (DBG) log("Set CPHS MailBox with exception: "
967                            + ar.exception);
968                }
969                if (ar.userObj != null) {
970                    if (DBG) log("Callback with CPHS MB successful.");
971                    AsyncResult.forMessage(((Message) ar.userObj)).exception
972                            = ar.exception;
973                    ((Message) ar.userObj).sendToTarget();
974                }
975                break;
976            case EVENT_SIM_REFRESH:
977                isRecordLoadResponse = false;
978                ar = (AsyncResult)msg.obj;
979		if (DBG) log("Sim REFRESH with exception: " + ar.exception);
980                if (ar.exception == null) {
981                    handleSimRefresh((int[])(ar.result));
982                }
983                break;
984            case EVENT_GET_CFIS_DONE:
985                isRecordLoadResponse = true;
986
987                ar = (AsyncResult)msg.obj;
988                data = (byte[])ar.result;
989
990                if (ar.exception != null) {
991                    break;
992                }
993
994                Log.d(LOG_TAG, "EF_CFIS: " +
995                   IccUtils.bytesToHexString(data));
996
997                mEfCfis = data;
998
999                // Refer TS 51.011 Section 10.3.46 for the content description
1000                callForwardingEnabled = ((data[1] & 0x01) != 0);
1001
1002                ((GSMPhone) phone).notifyCallForwardingIndicator();
1003                break;
1004
1005        }}catch (RuntimeException exc) {
1006            // I don't want these exceptions to be fatal
1007            Log.w(LOG_TAG, "Exception parsing SIM record", exc);
1008        } finally {
1009            // Count up record load responses even if they are fails
1010            if (isRecordLoadResponse) {
1011                onRecordLoaded();
1012            }
1013        }
1014    }
1015
1016    private void handleFileUpdate(int efid) {
1017        switch(efid) {
1018            case EF_MBDN:
1019                recordsToLoad++;
1020                new AdnRecordLoader(phone).loadFromEF(EF_MBDN, EF_EXT6,
1021                        mailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE));
1022                break;
1023            case EF_MAILBOX_CPHS:
1024                recordsToLoad++;
1025                new AdnRecordLoader(phone).loadFromEF(EF_MAILBOX_CPHS, EF_EXT1,
1026                        1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
1027                break;
1028            default:
1029                // For now, fetch all records if this is not a
1030                // voicemail number.
1031                // TODO: Handle other cases, instead of fetching all.
1032                adnCache.reset();
1033                fetchSimRecords();
1034                break;
1035        }
1036    }
1037
1038    private void handleSimRefresh(int[] result) {
1039        if (result == null || result.length == 0) {
1040	    if (DBG) log("handleSimRefresh without input");
1041            return;
1042        }
1043
1044        switch ((result[0])) {
1045            case CommandsInterface.SIM_REFRESH_FILE_UPDATED:
1046 		if (DBG) log("handleSimRefresh with SIM_REFRESH_FILE_UPDATED");
1047                // result[1] contains the EFID of the updated file.
1048                int efid = result[1];
1049                handleFileUpdate(efid);
1050                break;
1051            case CommandsInterface.SIM_REFRESH_INIT:
1052		if (DBG) log("handleSimRefresh with SIM_REFRESH_INIT");
1053                // need to reload all files (that we care about)
1054                adnCache.reset();
1055                fetchSimRecords();
1056                break;
1057            case CommandsInterface.SIM_REFRESH_RESET:
1058		if (DBG) log("handleSimRefresh with SIM_REFRESH_RESET");
1059                phone.mCM.setRadioPower(false, null);
1060                /* Note: no need to call setRadioPower(true).  Assuming the desired
1061                * radio power state is still ON (as tracked by ServiceStateTracker),
1062                * ServiceStateTracker will call setRadioPower when it receives the
1063                * RADIO_STATE_CHANGED notification for the power off.  And if the
1064                * desired power state has changed in the interim, we don't want to
1065                * override it with an unconditional power on.
1066                */
1067                break;
1068            default:
1069                // unknown refresh operation
1070		if (DBG) log("handleSimRefresh with unknown operation");
1071                break;
1072        }
1073    }
1074
1075    private void handleSms(byte[] ba) {
1076        if (ba[0] != 0)
1077            Log.d("ENF", "status : " + ba[0]);
1078
1079        // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1080        // 3 == "received by MS from network; message to be read"
1081        if (ba[0] == 3) {
1082            int n = ba.length;
1083
1084            // Note: Data may include trailing FF's.  That's OK; message
1085            // should still parse correctly.
1086            byte[] pdu = new byte[n - 1];
1087            System.arraycopy(ba, 1, pdu, 0, n - 1);
1088            SmsMessage message = SmsMessage.createFromPdu(pdu);
1089
1090            ((GSMPhone) phone).mSMS.dispatchMessage(message);
1091        }
1092    }
1093
1094
1095    private void handleSmses(ArrayList messages) {
1096        int count = messages.size();
1097
1098        for (int i = 0; i < count; i++) {
1099            byte[] ba = (byte[]) messages.get(i);
1100
1101            if (ba[0] != 0)
1102                Log.i("ENF", "status " + i + ": " + ba[0]);
1103
1104            // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1105            // 3 == "received by MS from network; message to be read"
1106
1107            if (ba[0] == 3) {
1108                int n = ba.length;
1109
1110                // Note: Data may include trailing FF's.  That's OK; message
1111                // should still parse correctly.
1112                byte[] pdu = new byte[n - 1];
1113                System.arraycopy(ba, 1, pdu, 0, n - 1);
1114                SmsMessage message = SmsMessage.createFromPdu(pdu);
1115
1116                ((GSMPhone) phone).mSMS.dispatchMessage(message);
1117
1118                // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1119                // 1 == "received by MS from network; message read"
1120
1121                ba[0] = 1;
1122
1123                if (false) { // XXX writing seems to crash RdoServD
1124                    phone.getIccFileHandler().updateEFLinearFixed(EF_SMS,
1125                            i, ba, null, obtainMessage(EVENT_MARK_SMS_READ_DONE, i));
1126                }
1127            }
1128        }
1129    }
1130
1131    protected void onRecordLoaded() {
1132        // One record loaded successfully or failed, In either case
1133        // we need to update the recordsToLoad count
1134        recordsToLoad -= 1;
1135
1136        if (recordsToLoad == 0 && recordsRequested == true) {
1137            onAllRecordsLoaded();
1138        } else if (recordsToLoad < 0) {
1139            Log.e(LOG_TAG, "SIMRecords: recordsToLoad <0, programmer error suspected");
1140            recordsToLoad = 0;
1141        }
1142    }
1143
1144    protected void onAllRecordsLoaded() {
1145        Log.d(LOG_TAG, "SIMRecords: record load complete");
1146
1147        String operator = getSIMOperatorNumeric();
1148
1149        // Some fields require more than one SIM record to set
1150
1151        phone.setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, operator);
1152
1153        if (imsi != null) {
1154            phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
1155                    MccTable.countryCodeForMcc(Integer.parseInt(imsi.substring(0,3))));
1156        }
1157        else {
1158            Log.e("SIM", "[SIMRecords] onAllRecordsLoaded: imsi is NULL!");
1159        }
1160
1161        setVoiceMailByCountry(operator);
1162        setSpnFromConfig(operator);
1163
1164        recordsLoadedRegistrants.notifyRegistrants(
1165            new AsyncResult(null, null, null));
1166        ((GSMPhone) phone).mSimCard.broadcastIccStateChangedIntent(
1167                SimCard.INTENT_VALUE_ICC_LOADED, null);
1168    }
1169
1170    //***** Private methods
1171
1172    private void setSpnFromConfig(String carrier) {
1173        if (mSpnOverride.containsCarrier(carrier)) {
1174            spn = mSpnOverride.getSpn(carrier);
1175        }
1176    }
1177
1178
1179    private void setVoiceMailByCountry (String spn) {
1180        if (mVmConfig.containsCarrier(spn)) {
1181            isVoiceMailFixed = true;
1182            voiceMailNum = mVmConfig.getVoiceMailNumber(spn);
1183            voiceMailTag = mVmConfig.getVoiceMailTag(spn);
1184        }
1185    }
1186
1187    private void onSimReady() {
1188        /* broadcast intent SIM_READY here so that we can make sure
1189          READY is sent before IMSI ready
1190        */
1191        ((GSMPhone) phone).mSimCard.broadcastIccStateChangedIntent(
1192                SimCard.INTENT_VALUE_ICC_READY, null);
1193
1194        fetchSimRecords();
1195    }
1196
1197    private void fetchSimRecords() {
1198        recordsRequested = true;
1199        IccFileHandler iccFh = phone.getIccFileHandler();
1200
1201        Log.v(LOG_TAG, "SIMRecords:fetchSimRecords " + recordsToLoad);
1202
1203        phone.mCM.getIMSI(obtainMessage(EVENT_GET_IMSI_DONE));
1204        recordsToLoad++;
1205
1206        iccFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
1207        recordsToLoad++;
1208
1209        // FIXME should examine EF[MSISDN]'s capability configuration
1210        // to determine which is the voice/data/fax line
1211        new AdnRecordLoader(phone).loadFromEF(EF_MSISDN, EF_EXT1, 1,
1212                    obtainMessage(EVENT_GET_MSISDN_DONE));
1213        recordsToLoad++;
1214
1215        // Record number is subscriber profile
1216        iccFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE));
1217        recordsToLoad++;
1218
1219        iccFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE));
1220        recordsToLoad++;
1221
1222        // Record number is subscriber profile
1223        iccFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE));
1224        recordsToLoad++;
1225
1226
1227        // Also load CPHS-style voice mail indicator, which stores
1228        // the same info as EF[MWIS]. If both exist, both are updated
1229        // but the EF[MWIS] data is preferred
1230        // Please note this must be loaded after EF[MWIS]
1231        iccFh.loadEFTransparent(
1232                EF_VOICE_MAIL_INDICATOR_CPHS,
1233                obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE));
1234        recordsToLoad++;
1235
1236        // Same goes for Call Forward Status indicator: fetch both
1237        // EF[CFIS] and CPHS-EF, with EF[CFIS] preferred.
1238        iccFh.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE));
1239        recordsToLoad++;
1240        iccFh.loadEFTransparent(EF_CFF_CPHS, obtainMessage(EVENT_GET_CFF_DONE));
1241        recordsToLoad++;
1242
1243
1244        getSpnFsm(true, null);
1245
1246        iccFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE));
1247        recordsToLoad++;
1248
1249        iccFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE));
1250        recordsToLoad++;
1251
1252        iccFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE));
1253        recordsToLoad++;
1254
1255        iccFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE));
1256        recordsToLoad++;
1257
1258        // XXX should seek instead of examining them all
1259        if (false) { // XXX
1260            iccFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE));
1261            recordsToLoad++;
1262        }
1263
1264        if (CRASH_RIL) {
1265            String sms = "0107912160130310f20404d0110041007030208054832b0120"
1266                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1267                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1268                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1269                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1270                         + "ffffffffffffffffffffffffffffff";
1271            byte[] ba = IccUtils.hexStringToBytes(sms);
1272
1273            iccFh.updateEFLinearFixed(EF_SMS, 1, ba, null,
1274                            obtainMessage(EVENT_MARK_SMS_READ_DONE, 1));
1275        }
1276    }
1277
1278    /**
1279     * Returns the SpnDisplayRule based on settings on the SIM and the
1280     * specified plmn (currently-registered PLMN).  See TS 22.101 Annex A
1281     * and TS 51.011 10.3.11 for details.
1282     *
1283     * If the SPN is not found on the SIM, the rule is always PLMN_ONLY.
1284     */
1285    protected int getDisplayRule(String plmn) {
1286        int rule;
1287        if (spn == null || spnDisplayCondition == -1) {
1288            // EF_SPN was not found on the SIM, or not yet loaded.  Just show ONS.
1289            rule = SPN_RULE_SHOW_PLMN;
1290        } else if (isOnMatchingPlmn(plmn)) {
1291            rule = SPN_RULE_SHOW_SPN;
1292            if ((spnDisplayCondition & 0x01) == 0x01) {
1293                // ONS required when registered to HPLMN or PLMN in EF_SPDI
1294                rule |= SPN_RULE_SHOW_PLMN;
1295            }
1296        } else {
1297            rule = SPN_RULE_SHOW_PLMN;
1298            if ((spnDisplayCondition & 0x02) == 0x00) {
1299                // SPN required if not registered to HPLMN or PLMN in EF_SPDI
1300                rule |= SPN_RULE_SHOW_SPN;
1301            }
1302        }
1303        return rule;
1304    }
1305
1306    /**
1307     * Checks if plmn is HPLMN or on the spdiNetworks list.
1308     */
1309    private boolean isOnMatchingPlmn(String plmn) {
1310        if (plmn == null) return false;
1311
1312        if (plmn.equals(getSIMOperatorNumeric())) {
1313            return true;
1314        }
1315
1316        if (spdiNetworks != null) {
1317            for (String spdiNet : spdiNetworks) {
1318                if (plmn.equals(spdiNet)) {
1319                    return true;
1320                }
1321            }
1322        }
1323        return false;
1324    }
1325
1326    /**
1327     * States of Get SPN Finite State Machine which only used by getSpnFsm()
1328     */
1329    private enum Get_Spn_Fsm_State {
1330        IDLE,               // No initialized
1331        INIT,               // Start FSM
1332        READ_SPN_3GPP,      // Load EF_SPN firstly
1333        READ_SPN_CPHS,      // Load EF_SPN_CPHS secondly
1334        READ_SPN_SHORT_CPHS // Load EF_SPN_SHORT_CPHS last
1335    }
1336
1337    /**
1338     * Finite State Machine to load Service Provider Name , which can be stored
1339     * in either EF_SPN (3GPP), EF_SPN_CPHS, or EF_SPN_SHORT_CPHS (CPHS4.2)
1340     *
1341     * After starting, FSM will search SPN EFs in order and stop after finding
1342     * the first valid SPN
1343     *
1344     * @param start set true only for initialize loading
1345     * @param ar the AsyncResult from loadEFTransparent
1346     *        ar.exception holds exception in error
1347     *        ar.result is byte[] for data in success
1348     */
1349    private void getSpnFsm(boolean start, AsyncResult ar) {
1350        byte[] data;
1351
1352        if (start) {
1353            spnState = Get_Spn_Fsm_State.INIT;
1354        }
1355
1356        switch(spnState){
1357            case INIT:
1358                spn = null;
1359
1360                phone.getIccFileHandler().loadEFTransparent( EF_SPN,
1361                        obtainMessage(EVENT_GET_SPN_DONE));
1362                recordsToLoad++;
1363
1364                spnState = Get_Spn_Fsm_State.READ_SPN_3GPP;
1365                break;
1366            case READ_SPN_3GPP:
1367                if (ar != null && ar.exception == null) {
1368                    data = (byte[]) ar.result;
1369                    spnDisplayCondition = 0xff & data[0];
1370                    spn = IccUtils.adnStringFieldToString(data, 1, data.length - 1);
1371
1372                    if (DBG) log("Load EF_SPN: " + spn
1373                            + " spnDisplayCondition: " + spnDisplayCondition);
1374                    phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
1375
1376                    spnState = Get_Spn_Fsm_State.IDLE;
1377                } else {
1378                    phone.getIccFileHandler().loadEFTransparent( EF_SPN_CPHS,
1379                            obtainMessage(EVENT_GET_SPN_DONE));
1380                    recordsToLoad++;
1381
1382                    spnState = Get_Spn_Fsm_State.READ_SPN_CPHS;
1383
1384                    // See TS 51.011 10.3.11.  Basically, default to
1385                    // show PLMN always, and SPN also if roaming.
1386                    spnDisplayCondition = -1;
1387                }
1388                break;
1389            case READ_SPN_CPHS:
1390                if (ar != null && ar.exception == null) {
1391                    data = (byte[]) ar.result;
1392                    spn = IccUtils.adnStringFieldToString(
1393                            data, 0, data.length - 1 );
1394
1395                    if (DBG) log("Load EF_SPN_CPHS: " + spn);
1396                    phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
1397
1398                    spnState = Get_Spn_Fsm_State.IDLE;
1399                } else {
1400                    phone.getIccFileHandler().loadEFTransparent(
1401                            EF_SPN_SHORT_CPHS, obtainMessage(EVENT_GET_SPN_DONE));
1402                    recordsToLoad++;
1403
1404                    spnState = Get_Spn_Fsm_State.READ_SPN_SHORT_CPHS;
1405                }
1406                break;
1407            case READ_SPN_SHORT_CPHS:
1408                if (ar != null && ar.exception == null) {
1409                    data = (byte[]) ar.result;
1410                    spn = IccUtils.adnStringFieldToString(
1411                            data, 0, data.length - 1);
1412
1413                    if (DBG) log("Load EF_SPN_SHORT_CPHS: " + spn);
1414                    phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
1415                }else {
1416                    if (DBG) log("No SPN loaded in either CHPS or 3GPP");
1417                }
1418
1419                spnState = Get_Spn_Fsm_State.IDLE;
1420                break;
1421            default:
1422                spnState = Get_Spn_Fsm_State.IDLE;
1423        }
1424    }
1425
1426    /**
1427     * Parse TS 51.011 EF[SPDI] record
1428     * This record contains the list of numeric network IDs that
1429     * are treated specially when determining SPN display
1430     */
1431    private void
1432    parseEfSpdi(byte[] data) {
1433        SimTlv tlv = new SimTlv(data, 0, data.length);
1434
1435        byte[] plmnEntries = null;
1436
1437        for ( ; tlv.isValidObject() ; tlv.nextObject()) {
1438            // Skip SPDI tag, if existant
1439            if (tlv.getTag() == TAG_SPDI) {
1440              tlv = new SimTlv(tlv.getData(), 0, tlv.getData().length);
1441            }
1442            // There should only be one TAG_SPDI_PLMN_LIST
1443            if (tlv.getTag() == TAG_SPDI_PLMN_LIST) {
1444                plmnEntries = tlv.getData();
1445                break;
1446            }
1447        }
1448
1449        if (plmnEntries == null) {
1450            return;
1451        }
1452
1453        spdiNetworks = new ArrayList<String>(plmnEntries.length / 3);
1454
1455        for (int i = 0 ; i + 2 < plmnEntries.length ; i += 3) {
1456            String plmnCode;
1457            plmnCode = IccUtils.bcdToString(plmnEntries, i, 3);
1458
1459            // Valid operator codes are 5 or 6 digits
1460            if (plmnCode.length() >= 5) {
1461                log("EF_SPDI network: " + plmnCode);
1462                spdiNetworks.add(plmnCode);
1463            }
1464        }
1465    }
1466
1467    /**
1468     * check to see if Mailbox Number is allocated and activated in CPHS SST
1469     */
1470    private boolean isCphsMailboxEnabled() {
1471        if (mCphsInfo == null)  return false;
1472        return ((mCphsInfo[1] & CPHS_SST_MBN_MASK) == CPHS_SST_MBN_ENABLED );
1473    }
1474
1475    protected void log(String s) {
1476        Log.d(LOG_TAG, "[SIMRecords] " + s);
1477    }
1478
1479}
1480