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