SIMRecords.java revision c46a085eedef8c7c59df4b182d90d787f1ff0f04
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;
41
42import java.util.ArrayList;
43
44
45/**
46 * {@hide}
47 */
48public class SIMRecords extends IccRecords {
49    protected static final String LOG_TAG = "GSM";
50
51    private static final boolean CRASH_RIL = false;
52
53    protected static final boolean DBG = true;
54
55    // ***** Instance Variables
56
57    VoiceMailConstants mVmConfig;
58
59
60    SpnOverride mSpnOverride;
61
62    // ***** Cached SIM State; cleared on channel close
63
64    private String imsi;
65    private boolean callForwardingEnabled;
66
67
68    /**
69     * States only used by getSpnFsm FSM
70     */
71    private Get_Spn_Fsm_State spnState;
72
73    /** CPHS service information (See CPHS 4.2 B.3.1.1)
74     *  It will be set in onSimReady if reading GET_CPHS_INFO successfully
75     *  mCphsInfo[0] is CPHS Phase
76     *  mCphsInfo[1] and mCphsInfo[2] is CPHS Service Table
77     */
78    private byte[] mCphsInfo = null;
79    boolean mCspPlmnEnabled = true;
80
81    byte[] efMWIS = null;
82    byte[] efCPHS_MWI =null;
83    byte[] mEfCff = null;
84    byte[] mEfCfis = null;
85
86
87    int spnDisplayCondition;
88    // Numeric network codes listed in TS 51.011 EF[SPDI]
89    ArrayList<String> spdiNetworks = null;
90
91    String pnnHomeName = null;
92
93    // ***** Constants
94
95    // Bitmasks for SPN display rules.
96    static final int SPN_RULE_SHOW_SPN  = 0x01;
97    static final int SPN_RULE_SHOW_PLMN = 0x02;
98
99    // From TS 51.011 EF[SPDI] section
100    static final int TAG_SPDI = 0xA3;
101    static final int TAG_SPDI_PLMN_LIST = 0x80;
102
103    // Full Name IEI from TS 24.008
104    static final int TAG_FULL_NETWORK_NAME = 0x43;
105
106    // Short Name IEI from TS 24.008
107    static final int TAG_SHORT_NETWORK_NAME = 0x45;
108
109    // active CFF from CPHS 4.2 B.4.5
110    static final int CFF_UNCONDITIONAL_ACTIVE = 0x0a;
111    static final int CFF_UNCONDITIONAL_DEACTIVE = 0x05;
112    static final int CFF_LINE1_MASK = 0x0f;
113    static final int CFF_LINE1_RESET = 0xf0;
114
115    // CPHS Service Table (See CPHS 4.2 B.3.1)
116    private static final int CPHS_SST_MBN_MASK = 0x30;
117    private static final int CPHS_SST_MBN_ENABLED = 0x30;
118
119    // ***** Event Constants
120
121    private static final int EVENT_SIM_READY = 1;
122    private static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 2;
123    protected static final int EVENT_GET_IMSI_DONE = 3;
124    protected static final int EVENT_GET_ICCID_DONE = 4;
125    private static final int EVENT_GET_MBI_DONE = 5;
126    private static final int EVENT_GET_MBDN_DONE = 6;
127    private static final int EVENT_GET_MWIS_DONE = 7;
128    private static final int EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE = 8;
129    protected static final int EVENT_GET_AD_DONE = 9; // Admin data on SIM
130    private static final int EVENT_GET_MSISDN_DONE = 10;
131    private static final int EVENT_GET_CPHS_MAILBOX_DONE = 11;
132    private static final int EVENT_GET_SPN_DONE = 12;
133    private static final int EVENT_GET_SPDI_DONE = 13;
134    private static final int EVENT_UPDATE_DONE = 14;
135    private static final int EVENT_GET_PNN_DONE = 15;
136    private static final int EVENT_GET_SST_DONE = 17;
137    private static final int EVENT_GET_ALL_SMS_DONE = 18;
138    private static final int EVENT_MARK_SMS_READ_DONE = 19;
139    private static final int EVENT_SET_MBDN_DONE = 20;
140    private static final int EVENT_SMS_ON_SIM = 21;
141    private static final int EVENT_GET_SMS_DONE = 22;
142    private static final int EVENT_GET_CFF_DONE = 24;
143    private static final int EVENT_SET_CPHS_MAILBOX_DONE = 25;
144    private static final int EVENT_GET_INFO_CPHS_DONE = 26;
145    private static final int EVENT_SET_MSISDN_DONE = 30;
146    private static final int EVENT_SIM_REFRESH = 31;
147    private static final int EVENT_GET_CFIS_DONE = 32;
148    private static final int EVENT_GET_CSP_CPHS_DONE = 33;
149
150    protected static final int CSIM_EVENT_BASE = 100;
151
152    // Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length.
153
154    private static final String[] MCCMNC_CODES_HAVING_3DIGITS_MNC = {
155        "405025", "405026", "405027", "405028", "405029", "405030", "405031", "405032",
156        "405033", "405034", "405035", "405036", "405037", "405038", "405039", "405040",
157        "405041", "405042", "405043", "405044", "405045", "405046", "405047", "405750",
158        "405751", "405752", "405753", "405754", "405755", "405756", "405799", "405800",
159        "405801", "405802", "405803", "405804", "405805", "405806", "405807", "405808",
160        "405809", "405810", "405811", "405812", "405813", "405814", "405815", "405816",
161        "405817", "405818", "405819", "405820", "405821", "405822", "405823", "405824",
162        "405825", "405826", "405827", "405828", "405829", "405830", "405831", "405832",
163        "405833", "405834", "405835", "405836", "405837", "405838", "405839", "405840",
164        "405841", "405842", "405843", "405844", "405845", "405846", "405847", "405848",
165        "405849", "405850", "405851", "405852", "405853", "405875", "405876", "405877",
166        "405878", "405879", "405880", "405881", "405882", "405883", "405884", "405885",
167        "405886", "405908", "405909", "405910", "405911", "405925", "405926", "405927",
168        "405928", "405929", "405932"
169    };
170
171    // ***** Constructor
172
173    public SIMRecords(PhoneBase p) {
174        super(p);
175
176        adnCache = new AdnRecordCache(phone);
177
178        mVmConfig = new VoiceMailConstants();
179        mSpnOverride = new SpnOverride();
180
181        recordsRequested = false;  // No load request is made till SIM ready
182
183        // recordsToLoad is set to 0 because no requests are made yet
184        recordsToLoad = 0;
185
186        // SIMRecord is used by CDMA+LTE mode, and SIM_READY event
187        // will be subscribed by CdmaLteServiceStateTracker.
188        if (phone.getLteOnCdmaMode() != Phone.LTE_ON_CDMA_TRUE) {
189            p.mCM.registerForSIMReady(this, EVENT_SIM_READY, null);
190        }
191        p.mCM.registerForOffOrNotAvailable(
192                        this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
193        p.mCM.setOnSmsOnSim(this, EVENT_SMS_ON_SIM, null);
194        p.mCM.registerForIccRefresh(this, EVENT_SIM_REFRESH, null);
195
196        // Start off by setting empty state
197        onRadioOffOrNotAvailable();
198
199    }
200
201    @Override
202    public void dispose() {
203        //Unregister for all events
204        if (phone.getLteOnCdmaMode() != Phone.LTE_ON_CDMA_TRUE) {
205            phone.mCM.unregisterForSIMReady(this);
206        }
207        phone.mCM.unregisterForOffOrNotAvailable( this);
208        phone.mCM.unregisterForIccRefresh(this);
209    }
210
211    protected void finalize() {
212        if(DBG) Log.d(LOG_TAG, "SIMRecords finalized");
213    }
214
215    protected void onRadioOffOrNotAvailable() {
216        imsi = null;
217        msisdn = null;
218        voiceMailNum = null;
219        countVoiceMessages = 0;
220        mncLength = UNINITIALIZED;
221        iccid = null;
222        // -1 means no EF_SPN found; treat accordingly.
223        spnDisplayCondition = -1;
224        efMWIS = null;
225        efCPHS_MWI = null;
226        spdiNetworks = null;
227        pnnHomeName = null;
228
229        adnCache.reset();
230
231        phone.setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, null);
232        phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, null);
233        phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, null);
234
235        // recordsRequested is set to false indicating that the SIM
236        // read requests made so far are not valid. This is set to
237        // true only when fresh set of read requests are made.
238        recordsRequested = false;
239    }
240
241
242    //***** Public Methods
243
244    /**
245     * {@inheritDoc}
246     */
247    @Override
248    public String getIMSI() {
249        return imsi;
250    }
251
252    public String getMsisdnNumber() {
253        return msisdn;
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                //Log.d(LOG_TAG, "SST: " + IccUtils.bytesToHexString(data));
972            break;
973
974            case EVENT_GET_INFO_CPHS_DONE:
975                isRecordLoadResponse = true;
976
977                ar = (AsyncResult)msg.obj;
978
979                if (ar.exception != null) {
980                    break;
981                }
982
983                mCphsInfo = (byte[])ar.result;
984
985                if (DBG) log("iCPHS: " + IccUtils.bytesToHexString(mCphsInfo));
986            break;
987
988            case EVENT_SET_MBDN_DONE:
989                isRecordLoadResponse = false;
990                ar = (AsyncResult)msg.obj;
991
992                if (ar.exception == null) {
993                    voiceMailNum = newVoiceMailNum;
994                    voiceMailTag = newVoiceMailTag;
995                }
996
997                if (isCphsMailboxEnabled()) {
998                    adn = new AdnRecord(voiceMailTag, voiceMailNum);
999                    Message onCphsCompleted = (Message) ar.userObj;
1000
1001                    /* write to cphs mailbox whenever it is available but
1002                    * we only need notify caller once if both updating are
1003                    * successful.
1004                    *
1005                    * so if set_mbdn successful, notify caller here and set
1006                    * onCphsCompleted to null
1007                    */
1008                    if (ar.exception == null && ar.userObj != null) {
1009                        AsyncResult.forMessage(((Message) ar.userObj)).exception
1010                                = null;
1011                        ((Message) ar.userObj).sendToTarget();
1012
1013                        if (DBG) log("Callback with MBDN successful.");
1014
1015                        onCphsCompleted = null;
1016                    }
1017
1018                    new AdnRecordLoader(phone).
1019                            updateEF(adn, EF_MAILBOX_CPHS, EF_EXT1, 1, null,
1020                            obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE,
1021                                    onCphsCompleted));
1022                } else {
1023                    if (ar.userObj != null) {
1024                        AsyncResult.forMessage(((Message) ar.userObj)).exception
1025                                = ar.exception;
1026                        ((Message) ar.userObj).sendToTarget();
1027                    }
1028                }
1029                break;
1030            case EVENT_SET_CPHS_MAILBOX_DONE:
1031                isRecordLoadResponse = false;
1032                ar = (AsyncResult)msg.obj;
1033                if(ar.exception == null) {
1034                    voiceMailNum = newVoiceMailNum;
1035                    voiceMailTag = newVoiceMailTag;
1036                } else {
1037                    if (DBG) log("Set CPHS MailBox with exception: "
1038                            + ar.exception);
1039                }
1040                if (ar.userObj != null) {
1041                    if (DBG) log("Callback with CPHS MB successful.");
1042                    AsyncResult.forMessage(((Message) ar.userObj)).exception
1043                            = ar.exception;
1044                    ((Message) ar.userObj).sendToTarget();
1045                }
1046                break;
1047            case EVENT_SIM_REFRESH:
1048                isRecordLoadResponse = false;
1049                ar = (AsyncResult)msg.obj;
1050		if (DBG) log("Sim REFRESH with exception: " + ar.exception);
1051                if (ar.exception == null) {
1052                    handleSimRefresh((int[])(ar.result));
1053                }
1054                break;
1055            case EVENT_GET_CFIS_DONE:
1056                isRecordLoadResponse = true;
1057
1058                ar = (AsyncResult)msg.obj;
1059                data = (byte[])ar.result;
1060
1061                if (ar.exception != null) {
1062                    break;
1063                }
1064
1065                Log.d(LOG_TAG, "EF_CFIS: " +
1066                   IccUtils.bytesToHexString(data));
1067
1068                mEfCfis = data;
1069
1070                // Refer TS 51.011 Section 10.3.46 for the content description
1071                callForwardingEnabled = ((data[1] & 0x01) != 0);
1072
1073                phone.notifyCallForwardingIndicator();
1074                break;
1075
1076            case EVENT_GET_CSP_CPHS_DONE:
1077                isRecordLoadResponse = true;
1078
1079                ar = (AsyncResult)msg.obj;
1080
1081                if (ar.exception != null) {
1082                    Log.e(LOG_TAG,"Exception in fetching EF_CSP data " + ar.exception);
1083                    break;
1084                }
1085
1086                data = (byte[])ar.result;
1087
1088                Log.i(LOG_TAG,"EF_CSP: " + IccUtils.bytesToHexString(data));
1089                handleEfCspData(data);
1090                break;
1091
1092        }}catch (RuntimeException exc) {
1093            // I don't want these exceptions to be fatal
1094            Log.w(LOG_TAG, "Exception parsing SIM record", exc);
1095        } finally {
1096            // Count up record load responses even if they are fails
1097            if (isRecordLoadResponse) {
1098                onRecordLoaded();
1099            }
1100        }
1101    }
1102
1103    private void handleFileUpdate(int efid) {
1104        switch(efid) {
1105            case EF_MBDN:
1106                recordsToLoad++;
1107                new AdnRecordLoader(phone).loadFromEF(EF_MBDN, EF_EXT6,
1108                        mailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE));
1109                break;
1110            case EF_MAILBOX_CPHS:
1111                recordsToLoad++;
1112                new AdnRecordLoader(phone).loadFromEF(EF_MAILBOX_CPHS, EF_EXT1,
1113                        1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
1114                break;
1115            case EF_CSP_CPHS:
1116                recordsToLoad++;
1117                Log.i(LOG_TAG, "[CSP] SIM Refresh for EF_CSP_CPHS");
1118                phone.getIccFileHandler().loadEFTransparent(EF_CSP_CPHS,
1119                        obtainMessage(EVENT_GET_CSP_CPHS_DONE));
1120                break;
1121            default:
1122                // For now, fetch all records if this is not a
1123                // voicemail number.
1124                // TODO: Handle other cases, instead of fetching all.
1125                adnCache.reset();
1126                fetchSimRecords();
1127                break;
1128        }
1129    }
1130
1131    private void handleSimRefresh(int[] result) {
1132        if (result == null || result.length == 0) {
1133	    if (DBG) log("handleSimRefresh without input");
1134            return;
1135        }
1136
1137        switch ((result[0])) {
1138            case CommandsInterface.SIM_REFRESH_FILE_UPDATED:
1139 		if (DBG) log("handleSimRefresh with SIM_REFRESH_FILE_UPDATED");
1140                // result[1] contains the EFID of the updated file.
1141                int efid = result[1];
1142                handleFileUpdate(efid);
1143                break;
1144            case CommandsInterface.SIM_REFRESH_INIT:
1145		if (DBG) log("handleSimRefresh with SIM_REFRESH_INIT");
1146                // need to reload all files (that we care about)
1147                adnCache.reset();
1148                fetchSimRecords();
1149                break;
1150            case CommandsInterface.SIM_REFRESH_RESET:
1151		if (DBG) log("handleSimRefresh with SIM_REFRESH_RESET");
1152                phone.mCM.setRadioPower(false, null);
1153                /* Note: no need to call setRadioPower(true).  Assuming the desired
1154                * radio power state is still ON (as tracked by ServiceStateTracker),
1155                * ServiceStateTracker will call setRadioPower when it receives the
1156                * RADIO_STATE_CHANGED notification for the power off.  And if the
1157                * desired power state has changed in the interim, we don't want to
1158                * override it with an unconditional power on.
1159                */
1160                break;
1161            default:
1162                // unknown refresh operation
1163		if (DBG) log("handleSimRefresh with unknown operation");
1164                break;
1165        }
1166    }
1167
1168    private void handleSms(byte[] ba) {
1169        if (ba[0] != 0)
1170            Log.d("ENF", "status : " + ba[0]);
1171
1172        // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1173        // 3 == "received by MS from network; message to be read"
1174        if (ba[0] == 3) {
1175            int n = ba.length;
1176
1177            // Note: Data may include trailing FF's.  That's OK; message
1178            // should still parse correctly.
1179            byte[] pdu = new byte[n - 1];
1180            System.arraycopy(ba, 1, pdu, 0, n - 1);
1181            SmsMessage message = SmsMessage.createFromPdu(pdu);
1182
1183            phone.mSMS.dispatchMessage(message);
1184        }
1185    }
1186
1187
1188    private void handleSmses(ArrayList messages) {
1189        int count = messages.size();
1190
1191        for (int i = 0; i < count; i++) {
1192            byte[] ba = (byte[]) messages.get(i);
1193
1194            if (ba[0] != 0)
1195                Log.i("ENF", "status " + i + ": " + ba[0]);
1196
1197            // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1198            // 3 == "received by MS from network; message to be read"
1199
1200            if (ba[0] == 3) {
1201                int n = ba.length;
1202
1203                // Note: Data may include trailing FF's.  That's OK; message
1204                // should still parse correctly.
1205                byte[] pdu = new byte[n - 1];
1206                System.arraycopy(ba, 1, pdu, 0, n - 1);
1207                SmsMessage message = SmsMessage.createFromPdu(pdu);
1208
1209                phone.mSMS.dispatchMessage(message);
1210
1211                // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1212                // 1 == "received by MS from network; message read"
1213
1214                ba[0] = 1;
1215
1216                if (false) { // XXX writing seems to crash RdoServD
1217                    phone.getIccFileHandler().updateEFLinearFixed(EF_SMS,
1218                            i, ba, null, obtainMessage(EVENT_MARK_SMS_READ_DONE, i));
1219                }
1220            }
1221        }
1222    }
1223
1224    protected void onRecordLoaded() {
1225        // One record loaded successfully or failed, In either case
1226        // we need to update the recordsToLoad count
1227        recordsToLoad -= 1;
1228
1229        if (recordsToLoad == 0 && recordsRequested == true) {
1230            onAllRecordsLoaded();
1231        } else if (recordsToLoad < 0) {
1232            Log.e(LOG_TAG, "SIMRecords: recordsToLoad <0, programmer error suspected");
1233            recordsToLoad = 0;
1234        }
1235    }
1236
1237    protected void onAllRecordsLoaded() {
1238        Log.d(LOG_TAG, "SIMRecords: record load complete");
1239
1240        String operator = getOperatorNumeric();
1241
1242        // Some fields require more than one SIM record to set
1243
1244        phone.setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, operator);
1245
1246        if (imsi != null) {
1247            phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
1248                    MccTable.countryCodeForMcc(Integer.parseInt(imsi.substring(0,3))));
1249        }
1250        else {
1251            Log.e("SIM", "[SIMRecords] onAllRecordsLoaded: imsi is NULL!");
1252        }
1253
1254        setVoiceMailByCountry(operator);
1255        setSpnFromConfig(operator);
1256
1257        recordsLoadedRegistrants.notifyRegistrants(
1258            new AsyncResult(null, null, null));
1259        phone.mIccCard.broadcastIccStateChangedIntent(
1260                SimCard.INTENT_VALUE_ICC_LOADED, null);
1261    }
1262
1263    //***** Private methods
1264
1265    private void setSpnFromConfig(String carrier) {
1266        if (mSpnOverride.containsCarrier(carrier)) {
1267            spn = mSpnOverride.getSpn(carrier);
1268        }
1269    }
1270
1271
1272    private void setVoiceMailByCountry (String spn) {
1273        if (mVmConfig.containsCarrier(spn)) {
1274            isVoiceMailFixed = true;
1275            voiceMailNum = mVmConfig.getVoiceMailNumber(spn);
1276            voiceMailTag = mVmConfig.getVoiceMailTag(spn);
1277        }
1278    }
1279
1280    public void onSimReady() {
1281        /* broadcast intent SIM_READY here so that we can make sure
1282          READY is sent before IMSI ready
1283        */
1284        phone.mIccCard.broadcastIccStateChangedIntent(
1285                SimCard.INTENT_VALUE_ICC_READY, null);
1286
1287        fetchSimRecords();
1288    }
1289
1290    protected void fetchSimRecords() {
1291        recordsRequested = true;
1292        IccFileHandler iccFh = phone.getIccFileHandler();
1293
1294        Log.v(LOG_TAG, "SIMRecords:fetchSimRecords " + recordsToLoad);
1295
1296        phone.mCM.getIMSI(obtainMessage(EVENT_GET_IMSI_DONE));
1297        recordsToLoad++;
1298
1299        iccFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
1300        recordsToLoad++;
1301
1302        // FIXME should examine EF[MSISDN]'s capability configuration
1303        // to determine which is the voice/data/fax line
1304        new AdnRecordLoader(phone).loadFromEF(EF_MSISDN, EF_EXT1, 1,
1305                    obtainMessage(EVENT_GET_MSISDN_DONE));
1306        recordsToLoad++;
1307
1308        // Record number is subscriber profile
1309        iccFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE));
1310        recordsToLoad++;
1311
1312        iccFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE));
1313        recordsToLoad++;
1314
1315        // Record number is subscriber profile
1316        iccFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE));
1317        recordsToLoad++;
1318
1319
1320        // Also load CPHS-style voice mail indicator, which stores
1321        // the same info as EF[MWIS]. If both exist, both are updated
1322        // but the EF[MWIS] data is preferred
1323        // Please note this must be loaded after EF[MWIS]
1324        iccFh.loadEFTransparent(
1325                EF_VOICE_MAIL_INDICATOR_CPHS,
1326                obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE));
1327        recordsToLoad++;
1328
1329        // Same goes for Call Forward Status indicator: fetch both
1330        // EF[CFIS] and CPHS-EF, with EF[CFIS] preferred.
1331        iccFh.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE));
1332        recordsToLoad++;
1333        iccFh.loadEFTransparent(EF_CFF_CPHS, obtainMessage(EVENT_GET_CFF_DONE));
1334        recordsToLoad++;
1335
1336
1337        getSpnFsm(true, null);
1338
1339        iccFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE));
1340        recordsToLoad++;
1341
1342        iccFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE));
1343        recordsToLoad++;
1344
1345        iccFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE));
1346        recordsToLoad++;
1347
1348        iccFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE));
1349        recordsToLoad++;
1350
1351        iccFh.loadEFTransparent(EF_CSP_CPHS,obtainMessage(EVENT_GET_CSP_CPHS_DONE));
1352        recordsToLoad++;
1353
1354        // XXX should seek instead of examining them all
1355        if (false) { // XXX
1356            iccFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE));
1357            recordsToLoad++;
1358        }
1359
1360        if (CRASH_RIL) {
1361            String sms = "0107912160130310f20404d0110041007030208054832b0120"
1362                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1363                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1364                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1365                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1366                         + "ffffffffffffffffffffffffffffff";
1367            byte[] ba = IccUtils.hexStringToBytes(sms);
1368
1369            iccFh.updateEFLinearFixed(EF_SMS, 1, ba, null,
1370                            obtainMessage(EVENT_MARK_SMS_READ_DONE, 1));
1371        }
1372    }
1373
1374    /**
1375     * Returns the SpnDisplayRule based on settings on the SIM and the
1376     * specified plmn (currently-registered PLMN).  See TS 22.101 Annex A
1377     * and TS 51.011 10.3.11 for details.
1378     *
1379     * If the SPN is not found on the SIM, the rule is always PLMN_ONLY.
1380     */
1381    @Override
1382    public int getDisplayRule(String plmn) {
1383        int rule;
1384        if (spn == null || spnDisplayCondition == -1) {
1385            // EF_SPN was not found on the SIM, or not yet loaded.  Just show ONS.
1386            rule = SPN_RULE_SHOW_PLMN;
1387        } else if (isOnMatchingPlmn(plmn)) {
1388            rule = SPN_RULE_SHOW_SPN;
1389            if ((spnDisplayCondition & 0x01) == 0x01) {
1390                // ONS required when registered to HPLMN or PLMN in EF_SPDI
1391                rule |= SPN_RULE_SHOW_PLMN;
1392            }
1393        } else {
1394            rule = SPN_RULE_SHOW_PLMN;
1395            if ((spnDisplayCondition & 0x02) == 0x00) {
1396                // SPN required if not registered to HPLMN or PLMN in EF_SPDI
1397                rule |= SPN_RULE_SHOW_SPN;
1398            }
1399        }
1400        return rule;
1401    }
1402
1403    /**
1404     * Checks if plmn is HPLMN or on the spdiNetworks list.
1405     */
1406    private boolean isOnMatchingPlmn(String plmn) {
1407        if (plmn == null) return false;
1408
1409        if (plmn.equals(getOperatorNumeric())) {
1410            return true;
1411        }
1412
1413        if (spdiNetworks != null) {
1414            for (String spdiNet : spdiNetworks) {
1415                if (plmn.equals(spdiNet)) {
1416                    return true;
1417                }
1418            }
1419        }
1420        return false;
1421    }
1422
1423    /**
1424     * States of Get SPN Finite State Machine which only used by getSpnFsm()
1425     */
1426    private enum Get_Spn_Fsm_State {
1427        IDLE,               // No initialized
1428        INIT,               // Start FSM
1429        READ_SPN_3GPP,      // Load EF_SPN firstly
1430        READ_SPN_CPHS,      // Load EF_SPN_CPHS secondly
1431        READ_SPN_SHORT_CPHS // Load EF_SPN_SHORT_CPHS last
1432    }
1433
1434    /**
1435     * Finite State Machine to load Service Provider Name , which can be stored
1436     * in either EF_SPN (3GPP), EF_SPN_CPHS, or EF_SPN_SHORT_CPHS (CPHS4.2)
1437     *
1438     * After starting, FSM will search SPN EFs in order and stop after finding
1439     * the first valid SPN
1440     *
1441     * @param start set true only for initialize loading
1442     * @param ar the AsyncResult from loadEFTransparent
1443     *        ar.exception holds exception in error
1444     *        ar.result is byte[] for data in success
1445     */
1446    private void getSpnFsm(boolean start, AsyncResult ar) {
1447        byte[] data;
1448
1449        if (start) {
1450            spnState = Get_Spn_Fsm_State.INIT;
1451        }
1452
1453        switch(spnState){
1454            case INIT:
1455                spn = null;
1456
1457                phone.getIccFileHandler().loadEFTransparent( EF_SPN,
1458                        obtainMessage(EVENT_GET_SPN_DONE));
1459                recordsToLoad++;
1460
1461                spnState = Get_Spn_Fsm_State.READ_SPN_3GPP;
1462                break;
1463            case READ_SPN_3GPP:
1464                if (ar != null && ar.exception == null) {
1465                    data = (byte[]) ar.result;
1466                    spnDisplayCondition = 0xff & data[0];
1467                    spn = IccUtils.adnStringFieldToString(data, 1, data.length - 1);
1468
1469                    if (DBG) log("Load EF_SPN: " + spn
1470                            + " spnDisplayCondition: " + spnDisplayCondition);
1471                    phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
1472
1473                    spnState = Get_Spn_Fsm_State.IDLE;
1474                } else {
1475                    phone.getIccFileHandler().loadEFTransparent( EF_SPN_CPHS,
1476                            obtainMessage(EVENT_GET_SPN_DONE));
1477                    recordsToLoad++;
1478
1479                    spnState = Get_Spn_Fsm_State.READ_SPN_CPHS;
1480
1481                    // See TS 51.011 10.3.11.  Basically, default to
1482                    // show PLMN always, and SPN also if roaming.
1483                    spnDisplayCondition = -1;
1484                }
1485                break;
1486            case READ_SPN_CPHS:
1487                if (ar != null && ar.exception == null) {
1488                    data = (byte[]) ar.result;
1489                    spn = IccUtils.adnStringFieldToString(
1490                            data, 0, data.length - 1 );
1491
1492                    if (DBG) log("Load EF_SPN_CPHS: " + spn);
1493                    phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
1494
1495                    spnState = Get_Spn_Fsm_State.IDLE;
1496                } else {
1497                    phone.getIccFileHandler().loadEFTransparent(
1498                            EF_SPN_SHORT_CPHS, obtainMessage(EVENT_GET_SPN_DONE));
1499                    recordsToLoad++;
1500
1501                    spnState = Get_Spn_Fsm_State.READ_SPN_SHORT_CPHS;
1502                }
1503                break;
1504            case READ_SPN_SHORT_CPHS:
1505                if (ar != null && ar.exception == null) {
1506                    data = (byte[]) ar.result;
1507                    spn = IccUtils.adnStringFieldToString(
1508                            data, 0, data.length - 1);
1509
1510                    if (DBG) log("Load EF_SPN_SHORT_CPHS: " + spn);
1511                    phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
1512                }else {
1513                    if (DBG) log("No SPN loaded in either CHPS or 3GPP");
1514                }
1515
1516                spnState = Get_Spn_Fsm_State.IDLE;
1517                break;
1518            default:
1519                spnState = Get_Spn_Fsm_State.IDLE;
1520        }
1521    }
1522
1523    /**
1524     * Parse TS 51.011 EF[SPDI] record
1525     * This record contains the list of numeric network IDs that
1526     * are treated specially when determining SPN display
1527     */
1528    private void
1529    parseEfSpdi(byte[] data) {
1530        SimTlv tlv = new SimTlv(data, 0, data.length);
1531
1532        byte[] plmnEntries = null;
1533
1534        for ( ; tlv.isValidObject() ; tlv.nextObject()) {
1535            // Skip SPDI tag, if existant
1536            if (tlv.getTag() == TAG_SPDI) {
1537              tlv = new SimTlv(tlv.getData(), 0, tlv.getData().length);
1538            }
1539            // There should only be one TAG_SPDI_PLMN_LIST
1540            if (tlv.getTag() == TAG_SPDI_PLMN_LIST) {
1541                plmnEntries = tlv.getData();
1542                break;
1543            }
1544        }
1545
1546        if (plmnEntries == null) {
1547            return;
1548        }
1549
1550        spdiNetworks = new ArrayList<String>(plmnEntries.length / 3);
1551
1552        for (int i = 0 ; i + 2 < plmnEntries.length ; i += 3) {
1553            String plmnCode;
1554            plmnCode = IccUtils.bcdToString(plmnEntries, i, 3);
1555
1556            // Valid operator codes are 5 or 6 digits
1557            if (plmnCode.length() >= 5) {
1558                log("EF_SPDI network: " + plmnCode);
1559                spdiNetworks.add(plmnCode);
1560            }
1561        }
1562    }
1563
1564    /**
1565     * check to see if Mailbox Number is allocated and activated in CPHS SST
1566     */
1567    private boolean isCphsMailboxEnabled() {
1568        if (mCphsInfo == null)  return false;
1569        return ((mCphsInfo[1] & CPHS_SST_MBN_MASK) == CPHS_SST_MBN_ENABLED );
1570    }
1571
1572    protected void log(String s) {
1573        Log.d(LOG_TAG, "[SIMRecords] " + s);
1574    }
1575
1576    /**
1577     * Return true if "Restriction of menu options for manual PLMN selection"
1578     * bit is set or EF_CSP data is unavailable, return false otherwise.
1579     */
1580    public boolean isCspPlmnEnabled() {
1581        return mCspPlmnEnabled;
1582    }
1583
1584    /**
1585     * Parse EF_CSP data and check if
1586     * "Restriction of menu options for manual PLMN selection" is
1587     * Enabled/Disabled
1588     *
1589     * @param data EF_CSP hex data.
1590     */
1591    private void handleEfCspData(byte[] data) {
1592        // As per spec CPHS4_2.WW6, CPHS B.4.7.1, EF_CSP contains CPHS defined
1593        // 18 bytes (i.e 9 service groups info) and additional data specific to
1594        // operator. The valueAddedServicesGroup is not part of standard
1595        // services. This is operator specific and can be programmed any where.
1596        // Normally this is programmed as 10th service after the standard
1597        // services.
1598        int usedCspGroups = data.length / 2;
1599        // This is the "Servive Group Number" of "Value Added Services Group".
1600        byte valueAddedServicesGroup = (byte)0xC0;
1601
1602        mCspPlmnEnabled = true;
1603        for (int i = 0; i < usedCspGroups; i++) {
1604             if (data[2 * i] == valueAddedServicesGroup) {
1605                 Log.i(LOG_TAG, "[CSP] found ValueAddedServicesGroup, value "
1606                       + data[(2 * i) + 1]);
1607                 if ((data[(2 * i) + 1] & 0x80) == 0x80) {
1608                     // Bit 8 is for
1609                     // "Restriction of menu options for manual PLMN selection".
1610                     // Operator Selection menu should be enabled.
1611                     mCspPlmnEnabled = true;
1612                 } else {
1613                     mCspPlmnEnabled = false;
1614                     // Operator Selection menu should be disabled.
1615                     // Operator Selection Mode should be set to Automatic.
1616                     Log.i(LOG_TAG,"[CSP] Set Automatic Network Selection");
1617                     phone.setNetworkSelectionModeAutomatic(null);
1618                 }
1619                 return;
1620             }
1621        }
1622
1623        Log.w(LOG_TAG, "[CSP] Value Added Service Group (0xC0), not found!");
1624    }
1625}
1626