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