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