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