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