Update mRecordsRequested on sending request instead of onLocked.
[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         }
1491     }
1492
1493
1494     private void setVoiceMailByCountry (String spn) {
1495         if (mVmConfig.containsCarrier(spn)) {
1496             mIsVoiceMailFixed = true;
1497             mVoiceMailNum = mVmConfig.getVoiceMailNumber(spn);
1498             mVoiceMailTag = mVmConfig.getVoiceMailTag(spn);
1499         }
1500     }
1501
1502     @Override
1503     public void onReady() {
1504         fetchSimRecords();
1505     }
1506
1507     private void onLocked() {
1508         if (DBG) log("only fetch EF_LI and EF_PL in lock state");
1509         loadEfLiAndEfPl();
1510     }
1511
1512     private void loadEfLiAndEfPl() {
1513         if (mParentApp.getType() == AppType.APPTYPE_USIM) {
1514             mRecordsRequested = true;
1515             mFh.loadEFTransparent(EF_LI,
1516                     obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfUsimLiLoaded()));
1517             mRecordsToLoad++;
1518
1519             mFh.loadEFTransparent(EF_PL,
1520                     obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfPlLoaded()));
1521             mRecordsToLoad++;
1522         }
1523     }
1524
1525     protected void fetchSimRecords() {
1526         mRecordsRequested = true;
1527
1528         if (DBG) log("fetchSimRecords " + mRecordsToLoad);
1529
1530         mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));
1531         mRecordsToLoad++;
1532
1533         mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
1534         mRecordsToLoad++;
1535
1536         // FIXME should examine EF[MSISDN]'s capability configuration
1537         // to determine which is the voice/data/fax line
1538         new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, EF_EXT1, 1,
1539                     obtainMessage(EVENT_GET_MSISDN_DONE));
1540         mRecordsToLoad++;
1541
1542         // Record number is subscriber profile
1543         mFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE));
1544         mRecordsToLoad++;
1545
1546         mFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE));
1547         mRecordsToLoad++;
1548
1549         // Record number is subscriber profile
1550         mFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE));
1551         mRecordsToLoad++;
1552
1553
1554         // Also load CPHS-style voice mail indicator, which stores
1555         // the same info as EF[MWIS]. If both exist, both are updated
1556         // but the EF[MWIS] data is preferred
1557         // Please note this must be loaded after EF[MWIS]
1558         mFh.loadEFTransparent(
1559                 EF_VOICE_MAIL_INDICATOR_CPHS,
1560                 obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE));
1561         mRecordsToLoad++;
1562
1563         // Same goes for Call Forward Status indicator: fetch both
1564         // EF[CFIS] and CPHS-EF, with EF[CFIS] preferred.
1565         mFh.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE));
1566         mRecordsToLoad++;
1567         mFh.loadEFTransparent(EF_CFF_CPHS, obtainMessage(EVENT_GET_CFF_DONE));
1568         mRecordsToLoad++;
1569
1570
1571         getSpnFsm(true, null);
1572
1573         mFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE));
1574         mRecordsToLoad++;
1575
1576         mFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE));
1577         mRecordsToLoad++;
1578
1579         mFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE));
1580         mRecordsToLoad++;
1581
1582         mFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE));
1583         mRecordsToLoad++;
1584
1585         mFh.loadEFTransparent(EF_CSP_CPHS,obtainMessage(EVENT_GET_CSP_CPHS_DONE));
1586         mRecordsToLoad++;
1587
1588         mFh.loadEFTransparent(EF_GID1, obtainMessage(EVENT_GET_GID1_DONE));
1589         mRecordsToLoad++;
1590
1591         loadEfLiAndEfPl();
1592
1593         // XXX should seek instead of examining them all
1594         if (false) { // XXX
1595             mFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE));
1596             mRecordsToLoad++;
1597         }
1598
1599         if (CRASH_RIL) {
1600             String sms = "0107912160130310f20404d0110041007030208054832b0120"
1601                          + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1602                          + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1603                          + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1604                          + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1605                          + "ffffffffffffffffffffffffffffff";
1606             byte[] ba = IccUtils.hexStringToBytes(sms);
1607
1608             mFh.updateEFLinearFixed(EF_SMS, 1, ba, null,
1609                             obtainMessage(EVENT_MARK_SMS_READ_DONE, 1));
1610         }
1611         if (DBG) log("fetchSimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested);
1612     }
1613
1614     /**
1615      * Returns the SpnDisplayRule based on settings on the SIM and the
1616      * specified plmn (currently-registered PLMN).  See TS 22.101 Annex A
1617      * and TS 51.011 10.3.11 for details.
1618      *
1619      * If the SPN is not found on the SIM or is empty, the rule is
1620      * always PLMN_ONLY.
1621      */
1622     @Override
1623     public int getDisplayRule(String plmn) {
1624         int rule;
1625
1626         if (mParentApp != null && mParentApp.getUiccCard() != null &&
1627             mParentApp.getUiccCard().getOperatorBrandOverride() != null) {
1628         // If the operator has been overridden, treat it as the SPN file on the SIM did not exist.
1629             rule = SPN_RULE_SHOW_PLMN;
1630         } else if (TextUtils.isEmpty(getServiceProviderName()) || mSpnDisplayCondition == -1) {
1631             // No EF_SPN content was found on the SIM, or not yet loaded.  Just show ONS.
1632             rule = SPN_RULE_SHOW_PLMN;
1633         } else if (isOnMatchingPlmn(plmn)) {
1634             rule = SPN_RULE_SHOW_SPN;
1635             if ((mSpnDisplayCondition & 0x01) == 0x01) {
1636                 // ONS required when registered to HPLMN or PLMN in EF_SPDI
1637                 rule |= SPN_RULE_SHOW_PLMN;
1638             }
1639         } else {
1640             rule = SPN_RULE_SHOW_PLMN;
1641             if ((mSpnDisplayCondition & 0x02) == 0x00) {
1642                 // SPN required if not registered to HPLMN or PLMN in EF_SPDI
1643                 rule |= SPN_RULE_SHOW_SPN;
1644             }
1645         }
1646         return rule;
1647     }
1648
1649     /**
1650      * Checks if plmn is HPLMN or on the spdiNetworks list.
1651      */
1652     private boolean isOnMatchingPlmn(String plmn) {
1653         if (plmn == null) return false;
1654
1655         if (plmn.equals(getOperatorNumeric())) {
1656             return true;
1657         }
1658
1659         if (mSpdiNetworks != null) {
1660             for (String spdiNet : mSpdiNetworks) {
1661                 if (plmn.equals(spdiNet)) {
1662                     return true;
1663                 }
1664             }
1665         }
1666         return false;
1667     }
1668
1669     /**
1670      * States of Get SPN Finite State Machine which only used by getSpnFsm()
1671      */
1672     private enum GetSpnFsmState {
1673         IDLE,               // No initialized
1674         INIT,               // Start FSM
1675         READ_SPN_3GPP,      // Load EF_SPN firstly
1676         READ_SPN_CPHS,      // Load EF_SPN_CPHS secondly
1677         READ_SPN_SHORT_CPHS // Load EF_SPN_SHORT_CPHS last
1678     }
1679
1680     /**
1681      * Finite State Machine to load Service Provider Name , which can be stored
1682      * in either EF_SPN (3GPP), EF_SPN_CPHS, or EF_SPN_SHORT_CPHS (CPHS4.2)
1683      *
1684      * After starting, FSM will search SPN EFs in order and stop after finding
1685      * the first valid SPN
1686      *
1687      * If the FSM gets restart while waiting for one of
1688      * SPN EFs results (i.e. a SIM refresh occurs after issuing
1689      * read EF_CPHS_SPN), it will re-initialize only after
1690      * receiving and discarding the unfinished SPN EF result.
1691      *
1692      * @param start set true only for initialize loading
1693      * @param ar the AsyncResult from loadEFTransparent
1694      *        ar.exception holds exception in error
1695      *        ar.result is byte[] for data in success
1696      */
1697     private void getSpnFsm(boolean start, AsyncResult ar) {
1698         byte[] data;
1699
1700         if (start) {
1701             // Check previous state to see if there is outstanding
1702             // SPN read
1703             if(mSpnState == GetSpnFsmState.READ_SPN_3GPP ||
1704                mSpnState == GetSpnFsmState.READ_SPN_CPHS ||
1705                mSpnState == GetSpnFsmState.READ_SPN_SHORT_CPHS ||
1706                mSpnState == GetSpnFsmState.INIT) {
1707                 // Set INIT then return so the INIT code
1708                 // will run when the outstanding read done.
1709                 mSpnState = GetSpnFsmState.INIT;
1710                 return;
1711             } else {
1712                 mSpnState = GetSpnFsmState.INIT;
1713             }
1714         }
1715
1716         switch(mSpnState){
1717             case INIT:
1718                 setServiceProviderName(null);
1719
1720                 mFh.loadEFTransparent(EF_SPN,
1721                         obtainMessage(EVENT_GET_SPN_DONE));
1722                 mRecordsToLoad++;
1723
1724                 mSpnState = GetSpnFsmState.READ_SPN_3GPP;
1725                 break;
1726             case READ_SPN_3GPP:
1727                 if (ar != null && ar.exception == null) {
1728                     data = (byte[]) ar.result;
1729                     mSpnDisplayCondition = 0xff & data[0];
1730                     setServiceProviderName(IccUtils.adnStringFieldToString(
1731                             data, 1, data.length - 1));
1732
1733                     if (DBG) log("Load EF_SPN: " + getServiceProviderName()
1734                             + " spnDisplayCondition: " + mSpnDisplayCondition);
1735                     setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, getServiceProviderName());
1736
1737                     mSpnState = GetSpnFsmState.IDLE;
1738                 } else {
1739                     mFh.loadEFTransparent( EF_SPN_CPHS,
1740                             obtainMessage(EVENT_GET_SPN_DONE));
1741                     mRecordsToLoad++;
1742
1743                     mSpnState = GetSpnFsmState.READ_SPN_CPHS;
1744
1745                     // See TS 51.011 10.3.11.  Basically, default to
1746                     // show PLMN always, and SPN also if roaming.
1747                     mSpnDisplayCondition = -1;
1748                 }
1749                 break;
1750             case READ_SPN_CPHS:
1751                 if (ar != null && ar.exception == null) {
1752                     data = (byte[]) ar.result;
1753                     setServiceProviderName(IccUtils.adnStringFieldToString(data, 0, data.length));
1754
1755                     if (DBG) log("Load EF_SPN_CPHS: " + getServiceProviderName());
1756                     setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, getServiceProviderName());
1757
1758                     mSpnState = GetSpnFsmState.IDLE;
1759                 } else {
1760                     mFh.loadEFTransparent(
1761                             EF_SPN_SHORT_CPHS, obtainMessage(EVENT_GET_SPN_DONE));
1762                     mRecordsToLoad++;
1763
1764                     mSpnState = GetSpnFsmState.READ_SPN_SHORT_CPHS;
1765                 }
1766                 break;
1767             case READ_SPN_SHORT_CPHS:
1768                 if (ar != null && ar.exception == null) {
1769                     data = (byte[]) ar.result;
1770                     setServiceProviderName(IccUtils.adnStringFieldToString(data, 0, data.length));
1771
1772                     if (DBG) log("Load EF_SPN_SHORT_CPHS: " + getServiceProviderName());
1773                     setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, getServiceProviderName());
1774                 }else {
1775                     if (DBG) log("No SPN loaded in either CHPS or 3GPP");
1776                 }
1777
1778                 mSpnState = GetSpnFsmState.IDLE;
1779                 break;
1780             default:
1781                 mSpnState = GetSpnFsmState.IDLE;
1782         }
1783     }
1784
1785     /**
1786      * Parse TS 51.011 EF[SPDI] record
1787      * This record contains the list of numeric network IDs that
1788      * are treated specially when determining SPN display
1789      */
1790     private void
1791     parseEfSpdi(byte[] data) {
1792         SimTlv tlv = new SimTlv(data, 0, data.length);
1793
1794         byte[] plmnEntries = null;
1795
1796         for ( ; tlv.isValidObject() ; tlv.nextObject()) {
1797             // Skip SPDI tag, if existant
1798             if (tlv.getTag() == TAG_SPDI) {
1799               tlv = new SimTlv(tlv.getData(), 0, tlv.getData().length);
1800             }
1801             // There should only be one TAG_SPDI_PLMN_LIST
1802             if (tlv.getTag() == TAG_SPDI_PLMN_LIST) {
1803                 plmnEntries = tlv.getData();
1804                 break;
1805             }
1806         }
1807
1808         if (plmnEntries == null) {
1809             return;
1810         }
1811
1812         mSpdiNetworks = new ArrayList<String>(plmnEntries.length / 3);
1813
1814         for (int i = 0 ; i + 2 < plmnEntries.length ; i += 3) {
1815             String plmnCode;
1816             plmnCode = IccUtils.bcdToString(plmnEntries, i, 3);
1817
1818             // Valid operator codes are 5 or 6 digits
1819             if (plmnCode.length() >= 5) {
1820                 log("EF_SPDI network: " + plmnCode);
1821                 mSpdiNetworks.add(plmnCode);
1822             }
1823         }
1824     }
1825
1826     /**
1827      * check to see if Mailbox Number is allocated and activated in CPHS SST
1828      */
1829     private boolean isCphsMailboxEnabled() {
1830         if (mCphsInfo == null)  return false;
1831         return ((mCphsInfo[1] & CPHS_SST_MBN_MASK) == CPHS_SST_MBN_ENABLED );
1832     }
1833
1834     @Override
1835     protected void log(String s) {
1836         Rlog.d(LOG_TAG, "[SIMRecords] " + s);
1837     }
1838
1839     @Override
1840     protected void loge(String s) {
1841         Rlog.e(LOG_TAG, "[SIMRecords] " + s);
1842     }
1843
1844     protected void logw(String s, Throwable tr) {
1845         Rlog.w(LOG_TAG, "[SIMRecords] " + s, tr);
1846     }
1847
1848     protected void logv(String s) {
1849         Rlog.v(LOG_TAG, "[SIMRecords] " + s);
1850     }
1851
1852     /**
1853      * Return true if "Restriction of menu options for manual PLMN selection"
1854      * bit is set or EF_CSP data is unavailable, return false otherwise.
1855      */
1856     @Override
1857     public boolean isCspPlmnEnabled() {
1858         return mCspPlmnEnabled;
1859     }
1860
1861     /**
1862      * Parse EF_CSP data and check if
1863      * "Restriction of menu options for manual PLMN selection" is
1864      * Enabled/Disabled
1865      *
1866      * @param data EF_CSP hex data.
1867      */
1868     private void handleEfCspData(byte[] data) {
1869         // As per spec CPHS4_2.WW6, CPHS B.4.7.1, EF_CSP contains CPHS defined
1870         // 18 bytes (i.e 9 service groups info) and additional data specific to
1871         // operator. The valueAddedServicesGroup is not part of standard
1872         // services. This is operator specific and can be programmed any where.
1873         // Normally this is programmed as 10th service after the standard
1874         // services.
1875         int usedCspGroups = data.length / 2;
1876         // This is the "Service Group Number" of "Value Added Services Group".
1877         byte valueAddedServicesGroup = (byte)0xC0;
1878
1879         mCspPlmnEnabled = true;
1880         for (int i = 0; i < usedCspGroups; i++) {
1881              if (data[2 * i] == valueAddedServicesGroup) {
1882                  log("[CSP] found ValueAddedServicesGroup, value " + data[(2 * i) + 1]);
1883                  if ((data[(2 * i) + 1] & 0x80) == 0x80) {
1884                      // Bit 8 is for
1885                      // "Restriction of menu options for manual PLMN selection".
1886                      // Operator Selection menu should be enabled.
1887                      mCspPlmnEnabled = true;
1888                  } else {
1889                      mCspPlmnEnabled = false;
1890                      // Operator Selection menu should be disabled.
1891                      // Operator Selection Mode should be set to Automatic.
1892                      log("[CSP] Set Automatic Network Selection");
1893                      mNetworkSelectionModeAutomaticRegistrants.notifyRegistrants();
1894                  }
1895                  return;
1896              }
1897         }
1898
1899         log("[CSP] Value Added Service Group (0xC0), not found!");
1900     }
1901
1902     @Override
1903     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1904         pw.println("SIMRecords: " + this);
1905         pw.println(" extends:");
1906         super.dump(fd, pw, args);
1907         pw.println(" mVmConfig=" + mVmConfig);
1908         pw.println(" mSpnOverride=" + mSpnOverride);
1909         pw.println(" mCallForwardingEnabled=" + mCallForwardingEnabled);
1910         pw.println(" mSpnState=" + mSpnState);
1911         pw.println(" mCphsInfo=" + mCphsInfo);
1912         pw.println(" mCspPlmnEnabled=" + mCspPlmnEnabled);
1913         pw.println(" mEfMWIS[]=" + Arrays.toString(mEfMWIS));
1914         pw.println(" mEfCPHS_MWI[]=" + Arrays.toString(mEfCPHS_MWI));
1915         pw.println(" mEfCff[]=" + Arrays.toString(mEfCff));
1916         pw.println(" mEfCfis[]=" + Arrays.toString(mEfCfis));
1917         pw.println(" mSpnDisplayCondition=" + mSpnDisplayCondition);
1918         pw.println(" mSpdiNetworks[]=" + mSpdiNetworks);
1919         pw.println(" mPnnHomeName=" + mPnnHomeName);
1920         pw.println(" mUsimServiceTable=" + mUsimServiceTable);
1921         pw.println(" mGid1=" + mGid1);
1922         pw.flush();
1923     }
1924
1925     private void setSystemProperty(String key, String val) {
1926         // Update the system properties only in case NON-DSDS.
1927         // TODO: Shall have a better approach!
1928         if (!TelephonyManager.getDefault().isMultiSimEnabled()) {
1929             SystemProperties.set(key, val);
1930         }
1931     }
1932 }