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