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