SIMRecords.java revision 9d7d62801ddb206d2ea96d74864a9edfe54d2eee
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.setOnIccRefresh(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.unSetOnIccRefresh(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 || mncLength == UNINITIALIZED || mncLength == UNKNOWN) {
476            return null;
477        }
478
479        // Length = length of MCC + length of MNC
480        // length of mcc = 3 (TS 23.003 Section 2.2)
481        return imsi.substring(0, 3 + mncLength);
482    }
483
484    // ***** Overridden from Handler
485    public void handleMessage(Message msg) {
486        AsyncResult ar;
487        AdnRecord adn;
488
489        byte data[];
490
491        boolean isRecordLoadResponse = false;
492
493        try { switch (msg.what) {
494            case EVENT_SIM_READY:
495                onSimReady();
496            break;
497
498            case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
499                onRadioOffOrNotAvailable();
500            break;
501
502            /* IO events */
503            case EVENT_GET_IMSI_DONE:
504                isRecordLoadResponse = true;
505
506                ar = (AsyncResult)msg.obj;
507
508                if (ar.exception != null) {
509                    Log.e(LOG_TAG, "Exception querying IMSI, Exception:" + ar.exception);
510                    break;
511                }
512
513                imsi = (String) ar.result;
514
515                // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more
516                // than 15 (and usually 15).
517                if (imsi != null && (imsi.length() < 6 || imsi.length() > 15)) {
518                    Log.e(LOG_TAG, "invalid IMSI " + imsi);
519                    imsi = null;
520                }
521
522                Log.d(LOG_TAG, "IMSI: " + imsi.substring(0, 6) + "xxxxxxx");
523
524                if (((mncLength == UNKNOWN) || (mncLength == 2)) &&
525                        ((imsi != null) && (imsi.length() >= 6))) {
526                    String mccmncCode = imsi.substring(0, 6);
527                    for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
528                        if (mccmnc.equals(mccmncCode)) {
529                            mncLength = 3;
530                            break;
531                        }
532                    }
533                }
534
535                if (mncLength == UNKNOWN) {
536                    // the SIM has told us all it knows, but it didn't know the mnc length.
537                    // guess using the mcc
538                    try {
539                        int mcc = Integer.parseInt(imsi.substring(0,3));
540                        mncLength = MccTable.smallestDigitsMccForMnc(mcc);
541                    } catch (NumberFormatException e) {
542                        mncLength = UNKNOWN;
543                        Log.e(LOG_TAG, "SIMRecords: Corrupt IMSI!");
544                    }
545                }
546
547                if (mncLength != UNKNOWN && mncLength != UNINITIALIZED) {
548                    // finally have both the imsi and the mncLength and can parse the imsi properly
549                    MccTable.updateMccMncConfiguration(phone, imsi.substring(0, 3 + mncLength));
550                }
551                phone.mSimCard.broadcastIccStateChangedIntent(
552                        SimCard.INTENT_VALUE_ICC_IMSI, null);
553            break;
554
555            case EVENT_GET_MBI_DONE:
556                boolean isValidMbdn;
557                isRecordLoadResponse = true;
558
559                ar = (AsyncResult)msg.obj;
560                data = (byte[]) ar.result;
561
562                isValidMbdn = false;
563                if (ar.exception == null) {
564                    // Refer TS 51.011 Section 10.3.44 for content details
565                    Log.d(LOG_TAG, "EF_MBI: " +
566                            IccUtils.bytesToHexString(data));
567
568                    // Voice mail record number stored first
569                    mailboxIndex = (int)data[0] & 0xff;
570
571                    // check if dailing numbe id valid
572                    if (mailboxIndex != 0 && mailboxIndex != 0xff) {
573                        Log.d(LOG_TAG, "Got valid mailbox number for MBDN");
574                        isValidMbdn = true;
575                    }
576                }
577
578                // one more record to load
579                recordsToLoad += 1;
580
581                if (isValidMbdn) {
582                    // Note: MBDN was not included in NUM_OF_SIM_RECORDS_LOADED
583                    new AdnRecordLoader(phone).loadFromEF(EF_MBDN, EF_EXT6,
584                            mailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE));
585                } else {
586                    // If this EF not present, try mailbox as in CPHS standard
587                    // CPHS (CPHS4_2.WW6) is a european standard.
588                    new AdnRecordLoader(phone).loadFromEF(EF_MAILBOX_CPHS,
589                            EF_EXT1, 1,
590                            obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
591                }
592
593                break;
594            case EVENT_GET_CPHS_MAILBOX_DONE:
595            case EVENT_GET_MBDN_DONE:
596                //Resetting the voice mail number and voice mail tag to null
597                //as these should be updated from the data read from EF_MBDN.
598                //If they are not reset, incase of invalid data/exception these
599                //variables are retaining their previous values and are
600                //causing invalid voice mailbox info display to user.
601                voiceMailNum = null;
602                voiceMailTag = null;
603                isRecordLoadResponse = true;
604
605                ar = (AsyncResult)msg.obj;
606
607                if (ar.exception != null) {
608
609                    Log.d(LOG_TAG, "Invalid or missing EF"
610                        + ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? "[MAILBOX]" : "[MBDN]"));
611
612                    // Bug #645770 fall back to CPHS
613                    // FIXME should use SST to decide
614
615                    if (msg.what == EVENT_GET_MBDN_DONE) {
616                        //load CPHS on fail...
617                        // FIXME right now, only load line1's CPHS voice mail entry
618
619                        recordsToLoad += 1;
620                        new AdnRecordLoader(phone).loadFromEF(
621                                EF_MAILBOX_CPHS, EF_EXT1, 1,
622                                obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
623                    }
624                    break;
625                }
626
627                adn = (AdnRecord)ar.result;
628
629                Log.d(LOG_TAG, "VM: " + adn +
630                        ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? " EF[MAILBOX]" : " EF[MBDN]"));
631
632                if (adn.isEmpty() && msg.what == EVENT_GET_MBDN_DONE) {
633                    // Bug #645770 fall back to CPHS
634                    // FIXME should use SST to decide
635                    // FIXME right now, only load line1's CPHS voice mail entry
636                    recordsToLoad += 1;
637                    new AdnRecordLoader(phone).loadFromEF(
638                            EF_MAILBOX_CPHS, EF_EXT1, 1,
639                            obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
640
641                    break;
642                }
643
644                voiceMailNum = adn.getNumber();
645                voiceMailTag = adn.getAlphaTag();
646            break;
647
648            case EVENT_GET_MSISDN_DONE:
649                isRecordLoadResponse = true;
650
651                ar = (AsyncResult)msg.obj;
652
653                if (ar.exception != null) {
654                    Log.d(LOG_TAG, "Invalid or missing EF[MSISDN]");
655                    break;
656                }
657
658                adn = (AdnRecord)ar.result;
659
660                msisdn = adn.getNumber();
661                msisdnTag = adn.getAlphaTag();
662
663                Log.d(LOG_TAG, "MSISDN: " + /*msisdn*/ "xxxxxxx");
664            break;
665
666            case EVENT_SET_MSISDN_DONE:
667                isRecordLoadResponse = false;
668                ar = (AsyncResult)msg.obj;
669
670                if (ar.userObj != null) {
671                    AsyncResult.forMessage(((Message) ar.userObj)).exception
672                            = ar.exception;
673                    ((Message) ar.userObj).sendToTarget();
674                }
675                break;
676
677            case EVENT_GET_MWIS_DONE:
678                isRecordLoadResponse = true;
679
680                ar = (AsyncResult)msg.obj;
681                data = (byte[])ar.result;
682
683                if (ar.exception != null) {
684                    break;
685                }
686
687                Log.d(LOG_TAG, "EF_MWIS: " +
688                   IccUtils.bytesToHexString(data));
689
690                efMWIS = data;
691
692                if ((data[0] & 0xff) == 0xff) {
693                    Log.d(LOG_TAG, "SIMRecords: Uninitialized record MWIS");
694                    break;
695                }
696
697                // Refer TS 51.011 Section 10.3.45 for the content description
698                boolean voiceMailWaiting = ((data[0] & 0x01) != 0);
699                countVoiceMessages = data[1] & 0xff;
700
701                if (voiceMailWaiting && countVoiceMessages == 0) {
702                    // Unknown count = -1
703                    countVoiceMessages = -1;
704                }
705
706                phone.notifyMessageWaitingIndicator();
707            break;
708
709            case EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE:
710                isRecordLoadResponse = true;
711
712                ar = (AsyncResult)msg.obj;
713                data = (byte[])ar.result;
714
715                if (ar.exception != null) {
716                    break;
717                }
718
719                efCPHS_MWI = data;
720
721                // Use this data if the EF[MWIS] exists and
722                // has been loaded
723
724                if (efMWIS == null) {
725                    int indicator = (int)(data[0] & 0xf);
726
727                    // Refer CPHS4_2.WW6 B4.2.3
728                    if (indicator == 0xA) {
729                        // Unknown count = -1
730                        countVoiceMessages = -1;
731                    } else if (indicator == 0x5) {
732                        countVoiceMessages = 0;
733                    }
734
735                    phone.notifyMessageWaitingIndicator();
736                }
737            break;
738
739            case EVENT_GET_ICCID_DONE:
740                isRecordLoadResponse = true;
741
742                ar = (AsyncResult)msg.obj;
743                data = (byte[])ar.result;
744
745                if (ar.exception != null) {
746                    break;
747                }
748
749                iccid = IccUtils.bcdToString(data, 0, data.length);
750
751                Log.d(LOG_TAG, "iccid: " + iccid);
752
753            break;
754
755
756            case EVENT_GET_AD_DONE:
757                try {
758                    isRecordLoadResponse = true;
759
760                    ar = (AsyncResult)msg.obj;
761                    data = (byte[])ar.result;
762
763                    if (ar.exception != null) {
764                        break;
765                    }
766
767                    Log.d(LOG_TAG, "EF_AD: " +
768                            IccUtils.bytesToHexString(data));
769
770                    if (data.length < 3) {
771                        Log.d(LOG_TAG, "SIMRecords: Corrupt AD data on SIM");
772                        break;
773                    }
774
775                    if (data.length == 3) {
776                        Log.d(LOG_TAG, "SIMRecords: MNC length not present in EF_AD");
777                        break;
778                    }
779
780                    mncLength = (int)data[3] & 0xf;
781
782                    if (mncLength == 0xf) {
783                        mncLength = UNKNOWN;
784                    }
785                } finally {
786                    if (((mncLength == UNINITIALIZED) || (mncLength == UNKNOWN) ||
787                            (mncLength == 2)) && ((imsi != null) && (imsi.length() >= 6))) {
788                        String mccmncCode = imsi.substring(0, 6);
789                        for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
790                            if (mccmnc.equals(mccmncCode)) {
791                                mncLength = 3;
792                                break;
793                            }
794                        }
795                    }
796
797                    if (mncLength == UNKNOWN || mncLength == UNINITIALIZED) {
798                        if (imsi != null) {
799                            try {
800                                int mcc = Integer.parseInt(imsi.substring(0,3));
801
802                                mncLength = MccTable.smallestDigitsMccForMnc(mcc);
803                            } catch (NumberFormatException e) {
804                                mncLength = UNKNOWN;
805                                Log.e(LOG_TAG, "SIMRecords: Corrupt IMSI!");
806                            }
807                        } else {
808                            // Indicate we got this info, but it didn't contain the length.
809                            mncLength = UNKNOWN;
810
811                            Log.d(LOG_TAG, "SIMRecords: MNC length not present in EF_AD");
812                        }
813                    }
814                    if (imsi != null && mncLength != UNKNOWN) {
815                        // finally have both imsi and the length of the mnc and can parse
816                        // the imsi properly
817                        MccTable.updateMccMncConfiguration(phone, imsi.substring(0, 3 + mncLength));
818                    }
819                }
820            break;
821
822            case EVENT_GET_SPN_DONE:
823                isRecordLoadResponse = true;
824                ar = (AsyncResult) msg.obj;
825                getSpnFsm(false, ar);
826            break;
827
828            case EVENT_GET_CFF_DONE:
829                isRecordLoadResponse = true;
830
831                ar = (AsyncResult) msg.obj;
832                data = (byte[]) ar.result;
833
834                if (ar.exception != null) {
835                    break;
836                }
837
838                Log.d(LOG_TAG, "EF_CFF_CPHS: " +
839                        IccUtils.bytesToHexString(data));
840                mEfCff = data;
841
842                if (mEfCfis == null) {
843                    callForwardingEnabled =
844                        ((data[0] & CFF_LINE1_MASK) == CFF_UNCONDITIONAL_ACTIVE);
845
846                    phone.notifyCallForwardingIndicator();
847                }
848                break;
849
850            case EVENT_GET_SPDI_DONE:
851                isRecordLoadResponse = true;
852
853                ar = (AsyncResult)msg.obj;
854                data = (byte[])ar.result;
855
856                if (ar.exception != null) {
857                    break;
858                }
859
860                parseEfSpdi(data);
861            break;
862
863            case EVENT_UPDATE_DONE:
864                ar = (AsyncResult)msg.obj;
865                if (ar.exception != null) {
866                    Log.i(LOG_TAG, "SIMRecords update failed", ar.exception);
867                }
868            break;
869
870            case EVENT_GET_PNN_DONE:
871                isRecordLoadResponse = true;
872
873                ar = (AsyncResult)msg.obj;
874                data = (byte[])ar.result;
875
876                if (ar.exception != null) {
877                    break;
878                }
879
880                SimTlv tlv = new SimTlv(data, 0, data.length);
881
882                for ( ; tlv.isValidObject() ; tlv.nextObject()) {
883                    if (tlv.getTag() == TAG_FULL_NETWORK_NAME) {
884                        pnnHomeName
885                            = IccUtils.networkNameToString(
886                                tlv.getData(), 0, tlv.getData().length);
887                        break;
888                    }
889                }
890            break;
891
892            case EVENT_GET_ALL_SMS_DONE:
893                isRecordLoadResponse = true;
894
895                ar = (AsyncResult)msg.obj;
896                if (ar.exception != null)
897                    break;
898
899                handleSmses((ArrayList) ar.result);
900                break;
901
902            case EVENT_MARK_SMS_READ_DONE:
903                Log.i("ENF", "marked read: sms " + msg.arg1);
904                break;
905
906
907            case EVENT_SMS_ON_SIM:
908                isRecordLoadResponse = false;
909
910                ar = (AsyncResult)msg.obj;
911
912                int[] index = (int[])ar.result;
913
914                if (ar.exception != null || index.length != 1) {
915                    Log.e(LOG_TAG, "[SIMRecords] Error on SMS_ON_SIM with exp "
916                            + ar.exception + " length " + index.length);
917                } else {
918                    Log.d(LOG_TAG, "READ EF_SMS RECORD index=" + index[0]);
919                    phone.getIccFileHandler().loadEFLinearFixed(EF_SMS,index[0],
920                            obtainMessage(EVENT_GET_SMS_DONE));
921                }
922                break;
923
924            case EVENT_GET_SMS_DONE:
925                isRecordLoadResponse = false;
926                ar = (AsyncResult)msg.obj;
927                if (ar.exception == null) {
928                    handleSms((byte[])ar.result);
929                } else {
930                    Log.e(LOG_TAG, "[SIMRecords] Error on GET_SMS with exp "
931                            + ar.exception);
932                }
933                break;
934            case EVENT_GET_SST_DONE:
935                isRecordLoadResponse = true;
936
937                ar = (AsyncResult)msg.obj;
938                data = (byte[])ar.result;
939
940                if (ar.exception != null) {
941                    break;
942                }
943
944                //Log.d(LOG_TAG, "SST: " + IccUtils.bytesToHexString(data));
945            break;
946
947            case EVENT_GET_INFO_CPHS_DONE:
948                isRecordLoadResponse = true;
949
950                ar = (AsyncResult)msg.obj;
951
952                if (ar.exception != null) {
953                    break;
954                }
955
956                mCphsInfo = (byte[])ar.result;
957
958                if (DBG) log("iCPHS: " + IccUtils.bytesToHexString(mCphsInfo));
959            break;
960
961            case EVENT_SET_MBDN_DONE:
962                isRecordLoadResponse = false;
963                ar = (AsyncResult)msg.obj;
964
965                if (ar.exception == null) {
966                    voiceMailNum = newVoiceMailNum;
967                    voiceMailTag = newVoiceMailTag;
968                }
969
970                if (isCphsMailboxEnabled()) {
971                    adn = new AdnRecord(voiceMailTag, voiceMailNum);
972                    Message onCphsCompleted = (Message) ar.userObj;
973
974                    /* write to cphs mailbox whenever it is available but
975                    * we only need notify caller once if both updating are
976                    * successful.
977                    *
978                    * so if set_mbdn successful, notify caller here and set
979                    * onCphsCompleted to null
980                    */
981                    if (ar.exception == null && ar.userObj != null) {
982                        AsyncResult.forMessage(((Message) ar.userObj)).exception
983                                = null;
984                        ((Message) ar.userObj).sendToTarget();
985
986                        if (DBG) log("Callback with MBDN successful.");
987
988                        onCphsCompleted = null;
989                    }
990
991                    new AdnRecordLoader(phone).
992                            updateEF(adn, EF_MAILBOX_CPHS, EF_EXT1, 1, null,
993                            obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE,
994                                    onCphsCompleted));
995                } else {
996                    if (ar.userObj != null) {
997                        AsyncResult.forMessage(((Message) ar.userObj)).exception
998                                = ar.exception;
999                        ((Message) ar.userObj).sendToTarget();
1000                    }
1001                }
1002                break;
1003            case EVENT_SET_CPHS_MAILBOX_DONE:
1004                isRecordLoadResponse = false;
1005                ar = (AsyncResult)msg.obj;
1006                if(ar.exception == null) {
1007                    voiceMailNum = newVoiceMailNum;
1008                    voiceMailTag = newVoiceMailTag;
1009                } else {
1010                    if (DBG) log("Set CPHS MailBox with exception: "
1011                            + ar.exception);
1012                }
1013                if (ar.userObj != null) {
1014                    if (DBG) log("Callback with CPHS MB successful.");
1015                    AsyncResult.forMessage(((Message) ar.userObj)).exception
1016                            = ar.exception;
1017                    ((Message) ar.userObj).sendToTarget();
1018                }
1019                break;
1020            case EVENT_SIM_REFRESH:
1021                isRecordLoadResponse = false;
1022                ar = (AsyncResult)msg.obj;
1023		if (DBG) log("Sim REFRESH with exception: " + ar.exception);
1024                if (ar.exception == null) {
1025                    handleSimRefresh((int[])(ar.result));
1026                }
1027                break;
1028            case EVENT_GET_CFIS_DONE:
1029                isRecordLoadResponse = true;
1030
1031                ar = (AsyncResult)msg.obj;
1032                data = (byte[])ar.result;
1033
1034                if (ar.exception != null) {
1035                    break;
1036                }
1037
1038                Log.d(LOG_TAG, "EF_CFIS: " +
1039                   IccUtils.bytesToHexString(data));
1040
1041                mEfCfis = data;
1042
1043                // Refer TS 51.011 Section 10.3.46 for the content description
1044                callForwardingEnabled = ((data[1] & 0x01) != 0);
1045
1046                phone.notifyCallForwardingIndicator();
1047                break;
1048
1049            case EVENT_GET_CSP_CPHS_DONE:
1050                isRecordLoadResponse = true;
1051
1052                ar = (AsyncResult)msg.obj;
1053
1054                if (ar.exception != null) {
1055                    Log.e(LOG_TAG,"Exception in fetching EF_CSP data " + ar.exception);
1056                    break;
1057                }
1058
1059                data = (byte[])ar.result;
1060
1061                Log.i(LOG_TAG,"EF_CSP: " + IccUtils.bytesToHexString(data));
1062                handleEfCspData(data);
1063                break;
1064
1065        }}catch (RuntimeException exc) {
1066            // I don't want these exceptions to be fatal
1067            Log.w(LOG_TAG, "Exception parsing SIM record", exc);
1068        } finally {
1069            // Count up record load responses even if they are fails
1070            if (isRecordLoadResponse) {
1071                onRecordLoaded();
1072            }
1073        }
1074    }
1075
1076    private void handleFileUpdate(int efid) {
1077        switch(efid) {
1078            case EF_MBDN:
1079                recordsToLoad++;
1080                new AdnRecordLoader(phone).loadFromEF(EF_MBDN, EF_EXT6,
1081                        mailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE));
1082                break;
1083            case EF_MAILBOX_CPHS:
1084                recordsToLoad++;
1085                new AdnRecordLoader(phone).loadFromEF(EF_MAILBOX_CPHS, EF_EXT1,
1086                        1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
1087                break;
1088            case EF_CSP_CPHS:
1089                recordsToLoad++;
1090                Log.i(LOG_TAG, "[CSP] SIM Refresh for EF_CSP_CPHS");
1091                phone.getIccFileHandler().loadEFTransparent(EF_CSP_CPHS,
1092                        obtainMessage(EVENT_GET_CSP_CPHS_DONE));
1093                break;
1094            default:
1095                // For now, fetch all records if this is not a
1096                // voicemail number.
1097                // TODO: Handle other cases, instead of fetching all.
1098                adnCache.reset();
1099                fetchSimRecords();
1100                break;
1101        }
1102    }
1103
1104    private void handleSimRefresh(int[] result) {
1105        if (result == null || result.length == 0) {
1106	    if (DBG) log("handleSimRefresh without input");
1107            return;
1108        }
1109
1110        switch ((result[0])) {
1111            case CommandsInterface.SIM_REFRESH_FILE_UPDATED:
1112 		if (DBG) log("handleSimRefresh with SIM_REFRESH_FILE_UPDATED");
1113                // result[1] contains the EFID of the updated file.
1114                int efid = result[1];
1115                handleFileUpdate(efid);
1116                break;
1117            case CommandsInterface.SIM_REFRESH_INIT:
1118		if (DBG) log("handleSimRefresh with SIM_REFRESH_INIT");
1119                // need to reload all files (that we care about)
1120                adnCache.reset();
1121                fetchSimRecords();
1122                break;
1123            case CommandsInterface.SIM_REFRESH_RESET:
1124		if (DBG) log("handleSimRefresh with SIM_REFRESH_RESET");
1125                phone.mCM.setRadioPower(false, null);
1126                /* Note: no need to call setRadioPower(true).  Assuming the desired
1127                * radio power state is still ON (as tracked by ServiceStateTracker),
1128                * ServiceStateTracker will call setRadioPower when it receives the
1129                * RADIO_STATE_CHANGED notification for the power off.  And if the
1130                * desired power state has changed in the interim, we don't want to
1131                * override it with an unconditional power on.
1132                */
1133                break;
1134            default:
1135                // unknown refresh operation
1136		if (DBG) log("handleSimRefresh with unknown operation");
1137                break;
1138        }
1139    }
1140
1141    private void handleSms(byte[] ba) {
1142        if (ba[0] != 0)
1143            Log.d("ENF", "status : " + ba[0]);
1144
1145        // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1146        // 3 == "received by MS from network; message to be read"
1147        if (ba[0] == 3) {
1148            int n = ba.length;
1149
1150            // Note: Data may include trailing FF's.  That's OK; message
1151            // should still parse correctly.
1152            byte[] pdu = new byte[n - 1];
1153            System.arraycopy(ba, 1, pdu, 0, n - 1);
1154            SmsMessage message = SmsMessage.createFromPdu(pdu);
1155
1156            phone.mSMS.dispatchMessage(message);
1157        }
1158    }
1159
1160
1161    private void handleSmses(ArrayList messages) {
1162        int count = messages.size();
1163
1164        for (int i = 0; i < count; i++) {
1165            byte[] ba = (byte[]) messages.get(i);
1166
1167            if (ba[0] != 0)
1168                Log.i("ENF", "status " + i + ": " + ba[0]);
1169
1170            // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1171            // 3 == "received by MS from network; message to be read"
1172
1173            if (ba[0] == 3) {
1174                int n = ba.length;
1175
1176                // Note: Data may include trailing FF's.  That's OK; message
1177                // should still parse correctly.
1178                byte[] pdu = new byte[n - 1];
1179                System.arraycopy(ba, 1, pdu, 0, n - 1);
1180                SmsMessage message = SmsMessage.createFromPdu(pdu);
1181
1182                phone.mSMS.dispatchMessage(message);
1183
1184                // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1185                // 1 == "received by MS from network; message read"
1186
1187                ba[0] = 1;
1188
1189                if (false) { // XXX writing seems to crash RdoServD
1190                    phone.getIccFileHandler().updateEFLinearFixed(EF_SMS,
1191                            i, ba, null, obtainMessage(EVENT_MARK_SMS_READ_DONE, i));
1192                }
1193            }
1194        }
1195    }
1196
1197    protected void onRecordLoaded() {
1198        // One record loaded successfully or failed, In either case
1199        // we need to update the recordsToLoad count
1200        recordsToLoad -= 1;
1201
1202        if (recordsToLoad == 0 && recordsRequested == true) {
1203            onAllRecordsLoaded();
1204        } else if (recordsToLoad < 0) {
1205            Log.e(LOG_TAG, "SIMRecords: recordsToLoad <0, programmer error suspected");
1206            recordsToLoad = 0;
1207        }
1208    }
1209
1210    protected void onAllRecordsLoaded() {
1211        Log.d(LOG_TAG, "SIMRecords: record load complete");
1212
1213        String operator = getSIMOperatorNumeric();
1214
1215        // Some fields require more than one SIM record to set
1216
1217        phone.setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, operator);
1218
1219        if (imsi != null) {
1220            phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
1221                    MccTable.countryCodeForMcc(Integer.parseInt(imsi.substring(0,3))));
1222        }
1223        else {
1224            Log.e("SIM", "[SIMRecords] onAllRecordsLoaded: imsi is NULL!");
1225        }
1226
1227        setVoiceMailByCountry(operator);
1228        setSpnFromConfig(operator);
1229
1230        recordsLoadedRegistrants.notifyRegistrants(
1231            new AsyncResult(null, null, null));
1232        phone.mSimCard.broadcastIccStateChangedIntent(
1233                SimCard.INTENT_VALUE_ICC_LOADED, null);
1234    }
1235
1236    //***** Private methods
1237
1238    private void setSpnFromConfig(String carrier) {
1239        if (mSpnOverride.containsCarrier(carrier)) {
1240            spn = mSpnOverride.getSpn(carrier);
1241        }
1242    }
1243
1244
1245    private void setVoiceMailByCountry (String spn) {
1246        if (mVmConfig.containsCarrier(spn)) {
1247            isVoiceMailFixed = true;
1248            voiceMailNum = mVmConfig.getVoiceMailNumber(spn);
1249            voiceMailTag = mVmConfig.getVoiceMailTag(spn);
1250        }
1251    }
1252
1253    public void onSimReady() {
1254        /* broadcast intent SIM_READY here so that we can make sure
1255          READY is sent before IMSI ready
1256        */
1257        phone.mSimCard.broadcastIccStateChangedIntent(
1258                SimCard.INTENT_VALUE_ICC_READY, null);
1259
1260        fetchSimRecords();
1261    }
1262
1263    private void fetchSimRecords() {
1264        recordsRequested = true;
1265        IccFileHandler iccFh = phone.getIccFileHandler();
1266
1267        Log.v(LOG_TAG, "SIMRecords:fetchSimRecords " + recordsToLoad);
1268
1269        phone.mCM.getIMSI(obtainMessage(EVENT_GET_IMSI_DONE));
1270        recordsToLoad++;
1271
1272        iccFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
1273        recordsToLoad++;
1274
1275        // FIXME should examine EF[MSISDN]'s capability configuration
1276        // to determine which is the voice/data/fax line
1277        new AdnRecordLoader(phone).loadFromEF(EF_MSISDN, EF_EXT1, 1,
1278                    obtainMessage(EVENT_GET_MSISDN_DONE));
1279        recordsToLoad++;
1280
1281        // Record number is subscriber profile
1282        iccFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE));
1283        recordsToLoad++;
1284
1285        iccFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE));
1286        recordsToLoad++;
1287
1288        // Record number is subscriber profile
1289        iccFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE));
1290        recordsToLoad++;
1291
1292
1293        // Also load CPHS-style voice mail indicator, which stores
1294        // the same info as EF[MWIS]. If both exist, both are updated
1295        // but the EF[MWIS] data is preferred
1296        // Please note this must be loaded after EF[MWIS]
1297        iccFh.loadEFTransparent(
1298                EF_VOICE_MAIL_INDICATOR_CPHS,
1299                obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE));
1300        recordsToLoad++;
1301
1302        // Same goes for Call Forward Status indicator: fetch both
1303        // EF[CFIS] and CPHS-EF, with EF[CFIS] preferred.
1304        iccFh.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE));
1305        recordsToLoad++;
1306        iccFh.loadEFTransparent(EF_CFF_CPHS, obtainMessage(EVENT_GET_CFF_DONE));
1307        recordsToLoad++;
1308
1309
1310        getSpnFsm(true, null);
1311
1312        iccFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE));
1313        recordsToLoad++;
1314
1315        iccFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE));
1316        recordsToLoad++;
1317
1318        iccFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE));
1319        recordsToLoad++;
1320
1321        iccFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE));
1322        recordsToLoad++;
1323
1324        iccFh.loadEFTransparent(EF_CSP_CPHS,obtainMessage(EVENT_GET_CSP_CPHS_DONE));
1325        recordsToLoad++;
1326
1327        // XXX should seek instead of examining them all
1328        if (false) { // XXX
1329            iccFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE));
1330            recordsToLoad++;
1331        }
1332
1333        if (CRASH_RIL) {
1334            String sms = "0107912160130310f20404d0110041007030208054832b0120"
1335                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1336                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1337                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1338                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1339                         + "ffffffffffffffffffffffffffffff";
1340            byte[] ba = IccUtils.hexStringToBytes(sms);
1341
1342            iccFh.updateEFLinearFixed(EF_SMS, 1, ba, null,
1343                            obtainMessage(EVENT_MARK_SMS_READ_DONE, 1));
1344        }
1345    }
1346
1347    /**
1348     * Returns the SpnDisplayRule based on settings on the SIM and the
1349     * specified plmn (currently-registered PLMN).  See TS 22.101 Annex A
1350     * and TS 51.011 10.3.11 for details.
1351     *
1352     * If the SPN is not found on the SIM, the rule is always PLMN_ONLY.
1353     */
1354    protected int getDisplayRule(String plmn) {
1355        int rule;
1356        if (spn == null || spnDisplayCondition == -1) {
1357            // EF_SPN was not found on the SIM, or not yet loaded.  Just show ONS.
1358            rule = SPN_RULE_SHOW_PLMN;
1359        } else if (isOnMatchingPlmn(plmn)) {
1360            rule = SPN_RULE_SHOW_SPN;
1361            if ((spnDisplayCondition & 0x01) == 0x01) {
1362                // ONS required when registered to HPLMN or PLMN in EF_SPDI
1363                rule |= SPN_RULE_SHOW_PLMN;
1364            }
1365        } else {
1366            rule = SPN_RULE_SHOW_PLMN;
1367            if ((spnDisplayCondition & 0x02) == 0x00) {
1368                // SPN required if not registered to HPLMN or PLMN in EF_SPDI
1369                rule |= SPN_RULE_SHOW_SPN;
1370            }
1371        }
1372        return rule;
1373    }
1374
1375    /**
1376     * Checks if plmn is HPLMN or on the spdiNetworks list.
1377     */
1378    private boolean isOnMatchingPlmn(String plmn) {
1379        if (plmn == null) return false;
1380
1381        if (plmn.equals(getSIMOperatorNumeric())) {
1382            return true;
1383        }
1384
1385        if (spdiNetworks != null) {
1386            for (String spdiNet : spdiNetworks) {
1387                if (plmn.equals(spdiNet)) {
1388                    return true;
1389                }
1390            }
1391        }
1392        return false;
1393    }
1394
1395    /**
1396     * States of Get SPN Finite State Machine which only used by getSpnFsm()
1397     */
1398    private enum Get_Spn_Fsm_State {
1399        IDLE,               // No initialized
1400        INIT,               // Start FSM
1401        READ_SPN_3GPP,      // Load EF_SPN firstly
1402        READ_SPN_CPHS,      // Load EF_SPN_CPHS secondly
1403        READ_SPN_SHORT_CPHS // Load EF_SPN_SHORT_CPHS last
1404    }
1405
1406    /**
1407     * Finite State Machine to load Service Provider Name , which can be stored
1408     * in either EF_SPN (3GPP), EF_SPN_CPHS, or EF_SPN_SHORT_CPHS (CPHS4.2)
1409     *
1410     * After starting, FSM will search SPN EFs in order and stop after finding
1411     * the first valid SPN
1412     *
1413     * @param start set true only for initialize loading
1414     * @param ar the AsyncResult from loadEFTransparent
1415     *        ar.exception holds exception in error
1416     *        ar.result is byte[] for data in success
1417     */
1418    private void getSpnFsm(boolean start, AsyncResult ar) {
1419        byte[] data;
1420
1421        if (start) {
1422            spnState = Get_Spn_Fsm_State.INIT;
1423        }
1424
1425        switch(spnState){
1426            case INIT:
1427                spn = null;
1428
1429                phone.getIccFileHandler().loadEFTransparent( EF_SPN,
1430                        obtainMessage(EVENT_GET_SPN_DONE));
1431                recordsToLoad++;
1432
1433                spnState = Get_Spn_Fsm_State.READ_SPN_3GPP;
1434                break;
1435            case READ_SPN_3GPP:
1436                if (ar != null && ar.exception == null) {
1437                    data = (byte[]) ar.result;
1438                    spnDisplayCondition = 0xff & data[0];
1439                    spn = IccUtils.adnStringFieldToString(data, 1, data.length - 1);
1440
1441                    if (DBG) log("Load EF_SPN: " + spn
1442                            + " spnDisplayCondition: " + spnDisplayCondition);
1443                    phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
1444
1445                    spnState = Get_Spn_Fsm_State.IDLE;
1446                } else {
1447                    phone.getIccFileHandler().loadEFTransparent( EF_SPN_CPHS,
1448                            obtainMessage(EVENT_GET_SPN_DONE));
1449                    recordsToLoad++;
1450
1451                    spnState = Get_Spn_Fsm_State.READ_SPN_CPHS;
1452
1453                    // See TS 51.011 10.3.11.  Basically, default to
1454                    // show PLMN always, and SPN also if roaming.
1455                    spnDisplayCondition = -1;
1456                }
1457                break;
1458            case READ_SPN_CPHS:
1459                if (ar != null && ar.exception == null) {
1460                    data = (byte[]) ar.result;
1461                    spn = IccUtils.adnStringFieldToString(
1462                            data, 0, data.length - 1 );
1463
1464                    if (DBG) log("Load EF_SPN_CPHS: " + spn);
1465                    phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
1466
1467                    spnState = Get_Spn_Fsm_State.IDLE;
1468                } else {
1469                    phone.getIccFileHandler().loadEFTransparent(
1470                            EF_SPN_SHORT_CPHS, obtainMessage(EVENT_GET_SPN_DONE));
1471                    recordsToLoad++;
1472
1473                    spnState = Get_Spn_Fsm_State.READ_SPN_SHORT_CPHS;
1474                }
1475                break;
1476            case READ_SPN_SHORT_CPHS:
1477                if (ar != null && ar.exception == null) {
1478                    data = (byte[]) ar.result;
1479                    spn = IccUtils.adnStringFieldToString(
1480                            data, 0, data.length - 1);
1481
1482                    if (DBG) log("Load EF_SPN_SHORT_CPHS: " + spn);
1483                    phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
1484                }else {
1485                    if (DBG) log("No SPN loaded in either CHPS or 3GPP");
1486                }
1487
1488                spnState = Get_Spn_Fsm_State.IDLE;
1489                break;
1490            default:
1491                spnState = Get_Spn_Fsm_State.IDLE;
1492        }
1493    }
1494
1495    /**
1496     * Parse TS 51.011 EF[SPDI] record
1497     * This record contains the list of numeric network IDs that
1498     * are treated specially when determining SPN display
1499     */
1500    private void
1501    parseEfSpdi(byte[] data) {
1502        SimTlv tlv = new SimTlv(data, 0, data.length);
1503
1504        byte[] plmnEntries = null;
1505
1506        for ( ; tlv.isValidObject() ; tlv.nextObject()) {
1507            // Skip SPDI tag, if existant
1508            if (tlv.getTag() == TAG_SPDI) {
1509              tlv = new SimTlv(tlv.getData(), 0, tlv.getData().length);
1510            }
1511            // There should only be one TAG_SPDI_PLMN_LIST
1512            if (tlv.getTag() == TAG_SPDI_PLMN_LIST) {
1513                plmnEntries = tlv.getData();
1514                break;
1515            }
1516        }
1517
1518        if (plmnEntries == null) {
1519            return;
1520        }
1521
1522        spdiNetworks = new ArrayList<String>(plmnEntries.length / 3);
1523
1524        for (int i = 0 ; i + 2 < plmnEntries.length ; i += 3) {
1525            String plmnCode;
1526            plmnCode = IccUtils.bcdToString(plmnEntries, i, 3);
1527
1528            // Valid operator codes are 5 or 6 digits
1529            if (plmnCode.length() >= 5) {
1530                log("EF_SPDI network: " + plmnCode);
1531                spdiNetworks.add(plmnCode);
1532            }
1533        }
1534    }
1535
1536    /**
1537     * check to see if Mailbox Number is allocated and activated in CPHS SST
1538     */
1539    private boolean isCphsMailboxEnabled() {
1540        if (mCphsInfo == null)  return false;
1541        return ((mCphsInfo[1] & CPHS_SST_MBN_MASK) == CPHS_SST_MBN_ENABLED );
1542    }
1543
1544    protected void log(String s) {
1545        Log.d(LOG_TAG, "[SIMRecords] " + s);
1546    }
1547
1548    /**
1549     * Return true if "Restriction of menu options for manual PLMN selection"
1550     * bit is set or EF_CSP data is unavailable, return false otherwise.
1551     */
1552    public boolean isCspPlmnEnabled() {
1553        return mCspPlmnEnabled;
1554    }
1555
1556    /**
1557     * Parse EF_CSP data and check if
1558     * "Restriction of menu options for manual PLMN selection" is
1559     * Enabled/Disabled
1560     *
1561     * @param data EF_CSP hex data.
1562     */
1563    private void handleEfCspData(byte[] data) {
1564        // As per spec CPHS4_2.WW6, CPHS B.4.7.1, EF_CSP contains CPHS defined
1565        // 18 bytes (i.e 9 service groups info) and additional data specific to
1566        // operator. The valueAddedServicesGroup is not part of standard
1567        // services. This is operator specific and can be programmed any where.
1568        // Normally this is programmed as 10th service after the standard
1569        // services.
1570        int usedCspGroups = data.length / 2;
1571        // This is the "Servive Group Number" of "Value Added Services Group".
1572        byte valueAddedServicesGroup = (byte)0xC0;
1573
1574        mCspPlmnEnabled = true;
1575        for (int i = 0; i < usedCspGroups; i++) {
1576             if (data[2 * i] == valueAddedServicesGroup) {
1577                 Log.i(LOG_TAG, "[CSP] found ValueAddedServicesGroup, value "
1578                       + data[(2 * i) + 1]);
1579                 if ((data[(2 * i) + 1] & 0x80) == 0x80) {
1580                     // Bit 8 is for
1581                     // "Restriction of menu options for manual PLMN selection".
1582                     // Operator Selection menu should be enabled.
1583                     mCspPlmnEnabled = true;
1584                 } else {
1585                     mCspPlmnEnabled = false;
1586                     // Operator Selection menu should be disabled.
1587                     // Operator Selection Mode should be set to Automatic.
1588                     Log.i(LOG_TAG,"[CSP] Set Automatic Network Selection");
1589                     phone.setNetworkSelectionModeAutomatic(null);
1590                 }
1591                 return;
1592             }
1593        }
1594
1595        Log.w(LOG_TAG, "[CSP] Value Added Service Group (0xC0), not found!");
1596    }
1597}
1598