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