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