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