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