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