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