SIMRecords.java revision 652fe674a597e221274a3746118f15cf40c7dbb9
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 fowarding 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 (validEfCfis(mEfCfis)) {
900                    mCallForwardingEnabled =
901                        ((data[0] & CFF_LINE1_MASK) == CFF_UNCONDITIONAL_ACTIVE);
902
903                    mRecordsEventsRegistrants.notifyResult(EVENT_CFI);
904                } else {
905                    log("EVENT_GET_CFF_DONE: invalid mEfCfis="
906                            + IccUtils.bytesToHexString(mEfCfis));
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: callFordwardingEnabled=" + 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. Overridden for CDMA/LTE phones by
1229     * {@link com.android.internal.telephony.cdma.CdmaLteUiccRecords}
1230     * to send messages to the secondary 3GPP format SMS dispatcher.
1231     */
1232    protected int dispatchGsmMessage(SmsMessageBase message) {
1233        mNewSmsRegistrants.notifyResult(message);
1234        return 0;
1235    }
1236
1237    private void handleSms(byte[] ba) {
1238        if (ba[0] != 0)
1239            Rlog.d("ENF", "status : " + ba[0]);
1240
1241        // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1242        // 3 == "received by MS from network; message to be read"
1243        if (ba[0] == 3) {
1244            int n = ba.length;
1245
1246            // Note: Data may include trailing FF's.  That's OK; message
1247            // should still parse correctly.
1248            byte[] pdu = new byte[n - 1];
1249            System.arraycopy(ba, 1, pdu, 0, n - 1);
1250            SmsMessage message = SmsMessage.createFromPdu(pdu);
1251
1252            dispatchGsmMessage(message);
1253        }
1254    }
1255
1256
1257    private void handleSmses(ArrayList<byte[]> messages) {
1258        int count = messages.size();
1259
1260        for (int i = 0; i < count; i++) {
1261            byte[] ba = messages.get(i);
1262
1263            if (ba[0] != 0)
1264                Rlog.i("ENF", "status " + i + ": " + ba[0]);
1265
1266            // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1267            // 3 == "received by MS from network; message to be read"
1268
1269            if (ba[0] == 3) {
1270                int n = ba.length;
1271
1272                // Note: Data may include trailing FF's.  That's OK; message
1273                // should still parse correctly.
1274                byte[] pdu = new byte[n - 1];
1275                System.arraycopy(ba, 1, pdu, 0, n - 1);
1276                SmsMessage message = SmsMessage.createFromPdu(pdu);
1277
1278                dispatchGsmMessage(message);
1279
1280                // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
1281                // 1 == "received by MS from network; message read"
1282
1283                ba[0] = 1;
1284
1285                if (false) { // FIXME: writing seems to crash RdoServD
1286                    mFh.updateEFLinearFixed(EF_SMS,
1287                            i, ba, null, obtainMessage(EVENT_MARK_SMS_READ_DONE, i));
1288                }
1289            }
1290        }
1291    }
1292
1293    @Override
1294    protected void onRecordLoaded() {
1295        // One record loaded successfully or failed, In either case
1296        // we need to update the recordsToLoad count
1297        mRecordsToLoad -= 1;
1298        if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested);
1299
1300        if (mRecordsToLoad == 0 && mRecordsRequested == true) {
1301            onAllRecordsLoaded();
1302        } else if (mRecordsToLoad < 0) {
1303            loge("recordsToLoad <0, programmer error suspected");
1304            mRecordsToLoad = 0;
1305        }
1306    }
1307
1308    @Override
1309    protected void onAllRecordsLoaded() {
1310        String operator = getOperatorNumeric();
1311
1312        // Some fields require more than one SIM record to set
1313
1314        log("SIMRecords: onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" +
1315                operator + "'");
1316        SystemProperties.set(PROPERTY_ICC_OPERATOR_NUMERIC, operator);
1317
1318        if (mImsi != null) {
1319            SystemProperties.set(PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
1320                    MccTable.countryCodeForMcc(Integer.parseInt(mImsi.substring(0,3))));
1321        }
1322        else {
1323            loge("onAllRecordsLoaded: imsi is NULL!");
1324        }
1325
1326        setVoiceMailByCountry(operator);
1327        setSpnFromConfig(operator);
1328
1329        mRecordsLoadedRegistrants.notifyRegistrants(
1330            new AsyncResult(null, null, null));
1331    }
1332
1333    //***** Private methods
1334
1335    private void setSpnFromConfig(String carrier) {
1336        if (mSpnOverride.containsCarrier(carrier)) {
1337            mSpn = mSpnOverride.getSpn(carrier);
1338        }
1339    }
1340
1341
1342    private void setVoiceMailByCountry (String spn) {
1343        if (mVmConfig.containsCarrier(spn)) {
1344            mIsVoiceMailFixed = true;
1345            mVoiceMailNum = mVmConfig.getVoiceMailNumber(spn);
1346            mVoiceMailTag = mVmConfig.getVoiceMailTag(spn);
1347        }
1348    }
1349
1350    @Override
1351    public void onReady() {
1352        fetchSimRecords();
1353    }
1354
1355    protected void fetchSimRecords() {
1356        mRecordsRequested = true;
1357
1358        if (DBG) log("fetchSimRecords " + mRecordsToLoad);
1359
1360        mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));
1361        mRecordsToLoad++;
1362
1363        mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
1364        mRecordsToLoad++;
1365
1366        // FIXME should examine EF[MSISDN]'s capability configuration
1367        // to determine which is the voice/data/fax line
1368        new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, EF_EXT1, 1,
1369                    obtainMessage(EVENT_GET_MSISDN_DONE));
1370        mRecordsToLoad++;
1371
1372        // Record number is subscriber profile
1373        mFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE));
1374        mRecordsToLoad++;
1375
1376        mFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE));
1377        mRecordsToLoad++;
1378
1379        // Record number is subscriber profile
1380        mFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE));
1381        mRecordsToLoad++;
1382
1383
1384        // Also load CPHS-style voice mail indicator, which stores
1385        // the same info as EF[MWIS]. If both exist, both are updated
1386        // but the EF[MWIS] data is preferred
1387        // Please note this must be loaded after EF[MWIS]
1388        mFh.loadEFTransparent(
1389                EF_VOICE_MAIL_INDICATOR_CPHS,
1390                obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE));
1391        mRecordsToLoad++;
1392
1393        // Same goes for Call Forward Status indicator: fetch both
1394        // EF[CFIS] and CPHS-EF, with EF[CFIS] preferred.
1395        mFh.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE));
1396        mRecordsToLoad++;
1397        mFh.loadEFTransparent(EF_CFF_CPHS, obtainMessage(EVENT_GET_CFF_DONE));
1398        mRecordsToLoad++;
1399
1400
1401        getSpnFsm(true, null);
1402
1403        mFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE));
1404        mRecordsToLoad++;
1405
1406        mFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE));
1407        mRecordsToLoad++;
1408
1409        mFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE));
1410        mRecordsToLoad++;
1411
1412        mFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE));
1413        mRecordsToLoad++;
1414
1415        mFh.loadEFTransparent(EF_CSP_CPHS,obtainMessage(EVENT_GET_CSP_CPHS_DONE));
1416        mRecordsToLoad++;
1417
1418        mFh.loadEFTransparent(EF_GID1, obtainMessage(EVENT_GET_GID1_DONE));
1419        mRecordsToLoad++;
1420
1421        // XXX should seek instead of examining them all
1422        if (false) { // XXX
1423            mFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE));
1424            mRecordsToLoad++;
1425        }
1426
1427        if (CRASH_RIL) {
1428            String sms = "0107912160130310f20404d0110041007030208054832b0120"
1429                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1430                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1431                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1432                         + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1433                         + "ffffffffffffffffffffffffffffff";
1434            byte[] ba = IccUtils.hexStringToBytes(sms);
1435
1436            mFh.updateEFLinearFixed(EF_SMS, 1, ba, null,
1437                            obtainMessage(EVENT_MARK_SMS_READ_DONE, 1));
1438        }
1439        if (DBG) log("fetchSimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested);
1440    }
1441
1442    /**
1443     * Returns the SpnDisplayRule based on settings on the SIM and the
1444     * specified plmn (currently-registered PLMN).  See TS 22.101 Annex A
1445     * and TS 51.011 10.3.11 for details.
1446     *
1447     * If the SPN is not found on the SIM or is empty, the rule is
1448     * always PLMN_ONLY.
1449     */
1450    @Override
1451    public int getDisplayRule(String plmn) {
1452        int rule;
1453        if (TextUtils.isEmpty(mSpn) || mSpnDisplayCondition == -1) {
1454            // No EF_SPN content was found on the SIM, or not yet loaded.  Just show ONS.
1455            rule = SPN_RULE_SHOW_PLMN;
1456        } else if (isOnMatchingPlmn(plmn)) {
1457            rule = SPN_RULE_SHOW_SPN;
1458            if ((mSpnDisplayCondition & 0x01) == 0x01) {
1459                // ONS required when registered to HPLMN or PLMN in EF_SPDI
1460                rule |= SPN_RULE_SHOW_PLMN;
1461            }
1462        } else {
1463            rule = SPN_RULE_SHOW_PLMN;
1464            if ((mSpnDisplayCondition & 0x02) == 0x00) {
1465                // SPN required if not registered to HPLMN or PLMN in EF_SPDI
1466                rule |= SPN_RULE_SHOW_SPN;
1467            }
1468        }
1469        return rule;
1470    }
1471
1472    /**
1473     * Checks if plmn is HPLMN or on the spdiNetworks list.
1474     */
1475    private boolean isOnMatchingPlmn(String plmn) {
1476        if (plmn == null) return false;
1477
1478        if (plmn.equals(getOperatorNumeric())) {
1479            return true;
1480        }
1481
1482        if (mSpdiNetworks != null) {
1483            for (String spdiNet : mSpdiNetworks) {
1484                if (plmn.equals(spdiNet)) {
1485                    return true;
1486                }
1487            }
1488        }
1489        return false;
1490    }
1491
1492    /**
1493     * States of Get SPN Finite State Machine which only used by getSpnFsm()
1494     */
1495    private enum GetSpnFsmState {
1496        IDLE,               // No initialized
1497        INIT,               // Start FSM
1498        READ_SPN_3GPP,      // Load EF_SPN firstly
1499        READ_SPN_CPHS,      // Load EF_SPN_CPHS secondly
1500        READ_SPN_SHORT_CPHS // Load EF_SPN_SHORT_CPHS last
1501    }
1502
1503    /**
1504     * Finite State Machine to load Service Provider Name , which can be stored
1505     * in either EF_SPN (3GPP), EF_SPN_CPHS, or EF_SPN_SHORT_CPHS (CPHS4.2)
1506     *
1507     * After starting, FSM will search SPN EFs in order and stop after finding
1508     * the first valid SPN
1509     *
1510     * If the FSM gets restart while waiting for one of
1511     * SPN EFs results (i.e. a SIM refresh occurs after issuing
1512     * read EF_CPHS_SPN), it will re-initialize only after
1513     * receiving and discarding the unfinished SPN EF result.
1514     *
1515     * @param start set true only for initialize loading
1516     * @param ar the AsyncResult from loadEFTransparent
1517     *        ar.exception holds exception in error
1518     *        ar.result is byte[] for data in success
1519     */
1520    private void getSpnFsm(boolean start, AsyncResult ar) {
1521        byte[] data;
1522
1523        if (start) {
1524            // Check previous state to see if there is outstanding
1525            // SPN read
1526            if(mSpnState == GetSpnFsmState.READ_SPN_3GPP ||
1527               mSpnState == GetSpnFsmState.READ_SPN_CPHS ||
1528               mSpnState == GetSpnFsmState.READ_SPN_SHORT_CPHS ||
1529               mSpnState == GetSpnFsmState.INIT) {
1530                // Set INIT then return so the INIT code
1531                // will run when the outstanding read done.
1532                mSpnState = GetSpnFsmState.INIT;
1533                return;
1534            } else {
1535                mSpnState = GetSpnFsmState.INIT;
1536            }
1537        }
1538
1539        switch(mSpnState){
1540            case INIT:
1541                mSpn = null;
1542
1543                mFh.loadEFTransparent(EF_SPN,
1544                        obtainMessage(EVENT_GET_SPN_DONE));
1545                mRecordsToLoad++;
1546
1547                mSpnState = GetSpnFsmState.READ_SPN_3GPP;
1548                break;
1549            case READ_SPN_3GPP:
1550                if (ar != null && ar.exception == null) {
1551                    data = (byte[]) ar.result;
1552                    mSpnDisplayCondition = 0xff & data[0];
1553                    mSpn = IccUtils.adnStringFieldToString(data, 1, data.length - 1);
1554
1555                    if (DBG) log("Load EF_SPN: " + mSpn
1556                            + " spnDisplayCondition: " + mSpnDisplayCondition);
1557                    SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn);
1558
1559                    mSpnState = GetSpnFsmState.IDLE;
1560                } else {
1561                    mFh.loadEFTransparent( EF_SPN_CPHS,
1562                            obtainMessage(EVENT_GET_SPN_DONE));
1563                    mRecordsToLoad++;
1564
1565                    mSpnState = GetSpnFsmState.READ_SPN_CPHS;
1566
1567                    // See TS 51.011 10.3.11.  Basically, default to
1568                    // show PLMN always, and SPN also if roaming.
1569                    mSpnDisplayCondition = -1;
1570                }
1571                break;
1572            case READ_SPN_CPHS:
1573                if (ar != null && ar.exception == null) {
1574                    data = (byte[]) ar.result;
1575                    mSpn = IccUtils.adnStringFieldToString(data, 0, data.length);
1576
1577                    if (DBG) log("Load EF_SPN_CPHS: " + mSpn);
1578                    SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn);
1579
1580                    mSpnState = GetSpnFsmState.IDLE;
1581                } else {
1582                    mFh.loadEFTransparent(
1583                            EF_SPN_SHORT_CPHS, obtainMessage(EVENT_GET_SPN_DONE));
1584                    mRecordsToLoad++;
1585
1586                    mSpnState = GetSpnFsmState.READ_SPN_SHORT_CPHS;
1587                }
1588                break;
1589            case READ_SPN_SHORT_CPHS:
1590                if (ar != null && ar.exception == null) {
1591                    data = (byte[]) ar.result;
1592                    mSpn = IccUtils.adnStringFieldToString(data, 0, data.length);
1593
1594                    if (DBG) log("Load EF_SPN_SHORT_CPHS: " + mSpn);
1595                    SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn);
1596                }else {
1597                    if (DBG) log("No SPN loaded in either CHPS or 3GPP");
1598                }
1599
1600                mSpnState = GetSpnFsmState.IDLE;
1601                break;
1602            default:
1603                mSpnState = GetSpnFsmState.IDLE;
1604        }
1605    }
1606
1607    /**
1608     * Parse TS 51.011 EF[SPDI] record
1609     * This record contains the list of numeric network IDs that
1610     * are treated specially when determining SPN display
1611     */
1612    private void
1613    parseEfSpdi(byte[] data) {
1614        SimTlv tlv = new SimTlv(data, 0, data.length);
1615
1616        byte[] plmnEntries = null;
1617
1618        for ( ; tlv.isValidObject() ; tlv.nextObject()) {
1619            // Skip SPDI tag, if existant
1620            if (tlv.getTag() == TAG_SPDI) {
1621              tlv = new SimTlv(tlv.getData(), 0, tlv.getData().length);
1622            }
1623            // There should only be one TAG_SPDI_PLMN_LIST
1624            if (tlv.getTag() == TAG_SPDI_PLMN_LIST) {
1625                plmnEntries = tlv.getData();
1626                break;
1627            }
1628        }
1629
1630        if (plmnEntries == null) {
1631            return;
1632        }
1633
1634        mSpdiNetworks = new ArrayList<String>(plmnEntries.length / 3);
1635
1636        for (int i = 0 ; i + 2 < plmnEntries.length ; i += 3) {
1637            String plmnCode;
1638            plmnCode = IccUtils.bcdToString(plmnEntries, i, 3);
1639
1640            // Valid operator codes are 5 or 6 digits
1641            if (plmnCode.length() >= 5) {
1642                log("EF_SPDI network: " + plmnCode);
1643                mSpdiNetworks.add(plmnCode);
1644            }
1645        }
1646    }
1647
1648    /**
1649     * check to see if Mailbox Number is allocated and activated in CPHS SST
1650     */
1651    private boolean isCphsMailboxEnabled() {
1652        if (mCphsInfo == null)  return false;
1653        return ((mCphsInfo[1] & CPHS_SST_MBN_MASK) == CPHS_SST_MBN_ENABLED );
1654    }
1655
1656    @Override
1657    protected void log(String s) {
1658        Rlog.d(LOG_TAG, "[SIMRecords] " + s);
1659    }
1660
1661    @Override
1662    protected void loge(String s) {
1663        Rlog.e(LOG_TAG, "[SIMRecords] " + s);
1664    }
1665
1666    protected void logw(String s, Throwable tr) {
1667        Rlog.w(LOG_TAG, "[SIMRecords] " + s, tr);
1668    }
1669
1670    protected void logv(String s) {
1671        Rlog.v(LOG_TAG, "[SIMRecords] " + s);
1672    }
1673
1674    /**
1675     * Return true if "Restriction of menu options for manual PLMN selection"
1676     * bit is set or EF_CSP data is unavailable, return false otherwise.
1677     */
1678    @Override
1679    public boolean isCspPlmnEnabled() {
1680        return mCspPlmnEnabled;
1681    }
1682
1683    /**
1684     * Parse EF_CSP data and check if
1685     * "Restriction of menu options for manual PLMN selection" is
1686     * Enabled/Disabled
1687     *
1688     * @param data EF_CSP hex data.
1689     */
1690    private void handleEfCspData(byte[] data) {
1691        // As per spec CPHS4_2.WW6, CPHS B.4.7.1, EF_CSP contains CPHS defined
1692        // 18 bytes (i.e 9 service groups info) and additional data specific to
1693        // operator. The valueAddedServicesGroup is not part of standard
1694        // services. This is operator specific and can be programmed any where.
1695        // Normally this is programmed as 10th service after the standard
1696        // services.
1697        int usedCspGroups = data.length / 2;
1698        // This is the "Servive Group Number" of "Value Added Services Group".
1699        byte valueAddedServicesGroup = (byte)0xC0;
1700
1701        mCspPlmnEnabled = true;
1702        for (int i = 0; i < usedCspGroups; i++) {
1703             if (data[2 * i] == valueAddedServicesGroup) {
1704                 log("[CSP] found ValueAddedServicesGroup, value " + data[(2 * i) + 1]);
1705                 if ((data[(2 * i) + 1] & 0x80) == 0x80) {
1706                     // Bit 8 is for
1707                     // "Restriction of menu options for manual PLMN selection".
1708                     // Operator Selection menu should be enabled.
1709                     mCspPlmnEnabled = true;
1710                 } else {
1711                     mCspPlmnEnabled = false;
1712                     // Operator Selection menu should be disabled.
1713                     // Operator Selection Mode should be set to Automatic.
1714                     log("[CSP] Set Automatic Network Selection");
1715                     mNetworkSelectionModeAutomaticRegistrants.notifyRegistrants();
1716                 }
1717                 return;
1718             }
1719        }
1720
1721        log("[CSP] Value Added Service Group (0xC0), not found!");
1722    }
1723
1724    @Override
1725    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1726        pw.println("SIMRecords: " + this);
1727        pw.println(" extends:");
1728        super.dump(fd, pw, args);
1729        pw.println(" mVmConfig=" + mVmConfig);
1730        pw.println(" mSpnOverride=" + mSpnOverride);
1731        pw.println(" mCallForwardingEnabled=" + mCallForwardingEnabled);
1732        pw.println(" mSpnState=" + mSpnState);
1733        pw.println(" mCphsInfo=" + mCphsInfo);
1734        pw.println(" mCspPlmnEnabled=" + mCspPlmnEnabled);
1735        pw.println(" mEfMWIS[]=" + Arrays.toString(mEfMWIS));
1736        pw.println(" mEfCPHS_MWI[]=" + Arrays.toString(mEfCPHS_MWI));
1737        pw.println(" mEfCff[]=" + Arrays.toString(mEfCff));
1738        pw.println(" mEfCfis[]=" + Arrays.toString(mEfCfis));
1739        pw.println(" mSpnDisplayCondition=" + mSpnDisplayCondition);
1740        pw.println(" mSpdiNetworks[]=" + mSpdiNetworks);
1741        pw.println(" mPnnHomeName=" + mPnnHomeName);
1742        pw.println(" mUsimServiceTable=" + mUsimServiceTable);
1743        pw.println(" mGid1=" + mGid1);
1744        pw.flush();
1745    }
1746}
1747