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