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