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