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