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