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