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