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