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