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