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