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