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