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