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