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