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