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