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