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