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