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