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