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