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