IccRecords.java revision 776e350a422070a0579900bfcc542766241c1414
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
17package com.android.internal.telephony.uicc;
18
19import android.content.Context;
20import android.os.AsyncResult;
21import android.os.Handler;
22import android.os.Message;
23import android.os.Registrant;
24import android.os.RegistrantList;
25
26import android.telephony.TelephonyManager;
27import com.android.internal.telephony.CommandsInterface;
28import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
29
30import java.io.FileDescriptor;
31import java.io.PrintWriter;
32import java.util.concurrent.atomic.AtomicBoolean;
33
34/**
35 * {@hide}
36 */
37public abstract class IccRecords extends Handler implements IccConstants {
38    protected static final boolean DBG = true;
39
40    // ***** Instance Variables
41    protected AtomicBoolean mDestroyed = new AtomicBoolean(false);
42    protected Context mContext;
43    protected CommandsInterface mCi;
44    protected IccFileHandler mFh;
45    protected UiccCardApplication mParentApp;
46
47    protected RegistrantList mRecordsLoadedRegistrants = new RegistrantList();
48    protected RegistrantList mImsiReadyRegistrants = new RegistrantList();
49    protected RegistrantList mRecordsEventsRegistrants = new RegistrantList();
50    protected RegistrantList mNewSmsRegistrants = new RegistrantList();
51    protected RegistrantList mNetworkSelectionModeAutomaticRegistrants = new RegistrantList();
52
53    protected int mRecordsToLoad;  // number of pending load requests
54
55    protected AdnRecordCache mAdnCache;
56
57    // ***** Cached SIM State; cleared on channel close
58
59    protected boolean mRecordsRequested = false; // true if we've made requests for the sim records
60
61    protected String mIccId;
62    protected String mMsisdn = null;  // My mobile number
63    protected String mMsisdnTag = null;
64    protected String mNewMsisdn = null;
65    protected String mNewMsisdnTag = null;
66    protected String mVoiceMailNum = null;
67    protected String mVoiceMailTag = null;
68    protected String mNewVoiceMailNum = null;
69    protected String mNewVoiceMailTag = null;
70    protected boolean mIsVoiceMailFixed = false;
71    protected String mImsi;
72    private IccIoResult auth_rsp;
73
74    protected int mMncLength = UNINITIALIZED;
75    protected int mMailboxIndex = 0; // 0 is no mailbox dailing number associated
76
77    private String mSpn;
78
79    protected String mGid1;
80
81    private final Object mLock = new Object();
82
83    // ***** Constants
84
85    // Markers for mncLength
86    protected static final int UNINITIALIZED = -1;
87    protected static final int UNKNOWN = 0;
88
89    // Bitmasks for SPN display rules.
90    public static final int SPN_RULE_SHOW_SPN  = 0x01;
91    public static final int SPN_RULE_SHOW_PLMN = 0x02;
92
93    // ***** Event Constants
94    protected static final int EVENT_SET_MSISDN_DONE = 30;
95    public static final int EVENT_MWI = 0; // Message Waiting indication
96    public static final int EVENT_CFI = 1; // Call Forwarding indication
97    public static final int EVENT_SPN = 2; // Service Provider Name
98
99    public static final int EVENT_GET_ICC_RECORD_DONE = 100;
100    protected static final int EVENT_APP_READY = 1;
101    private static final int EVENT_AKA_AUTHENTICATE_DONE          = 90;
102
103    @Override
104    public String toString() {
105        return "mDestroyed=" + mDestroyed
106                + " mContext=" + mContext
107                + " mCi=" + mCi
108                + " mFh=" + mFh
109                + " mParentApp=" + mParentApp
110                + " recordsLoadedRegistrants=" + mRecordsLoadedRegistrants
111                + " mImsiReadyRegistrants=" + mImsiReadyRegistrants
112                + " mRecordsEventsRegistrants=" + mRecordsEventsRegistrants
113                + " mNewSmsRegistrants=" + mNewSmsRegistrants
114                + " mNetworkSelectionModeAutomaticRegistrants="
115                        + mNetworkSelectionModeAutomaticRegistrants
116                + " recordsToLoad=" + mRecordsToLoad
117                + " adnCache=" + mAdnCache
118                + " recordsRequested=" + mRecordsRequested
119                + " iccid=" + mIccId
120                + " msisdn=" + mMsisdn
121                + " msisdnTag=" + mMsisdnTag
122                + " voiceMailNum=" + mVoiceMailNum
123                + " voiceMailTag=" + mVoiceMailTag
124                + " newVoiceMailNum=" + mNewVoiceMailNum
125                + " newVoiceMailTag=" + mNewVoiceMailTag
126                + " isVoiceMailFixed=" + mIsVoiceMailFixed
127                + " mImsi=" + mImsi
128                + " mncLength=" + mMncLength
129                + " mailboxIndex=" + mMailboxIndex
130                + " spn=" + mSpn;
131
132    }
133
134    /**
135     * Generic ICC record loaded callback. Subclasses can call EF load methods on
136     * {@link IccFileHandler} passing a Message for onLoaded with the what field set to
137     * {@link #EVENT_GET_ICC_RECORD_DONE} and the obj field set to an instance
138     * of this interface. The {@link #handleMessage} method in this class will print a
139     * log message using {@link #getEfName()} and decrement {@link #mRecordsToLoad}.
140     *
141     * If the record load was successful, {@link #onRecordLoaded} will be called with the result.
142     * Otherwise, an error log message will be output by {@link #handleMessage} and
143     * {@link #onRecordLoaded} will not be called.
144     */
145    public interface IccRecordLoaded {
146        String getEfName();
147        void onRecordLoaded(AsyncResult ar);
148    }
149
150    // ***** Constructor
151    public IccRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
152        mContext = c;
153        mCi = ci;
154        mFh = app.getIccFileHandler();
155        mParentApp = app;
156    }
157
158    /**
159     * Call when the IccRecords object is no longer going to be used.
160     */
161    public void dispose() {
162        mDestroyed.set(true);
163        mParentApp = null;
164        mFh = null;
165        mCi = null;
166        mContext = null;
167    }
168
169    public abstract void onReady();
170
171    //***** Public Methods
172    public AdnRecordCache getAdnCache() {
173        return mAdnCache;
174    }
175
176    public String getIccId() {
177        return mIccId;
178    }
179
180    public void registerForRecordsLoaded(Handler h, int what, Object obj) {
181        if (mDestroyed.get()) {
182            return;
183        }
184
185        Registrant r = new Registrant(h, what, obj);
186        mRecordsLoadedRegistrants.add(r);
187
188        if (mRecordsToLoad == 0 && mRecordsRequested == true) {
189            r.notifyRegistrant(new AsyncResult(null, null, null));
190        }
191    }
192    public void unregisterForRecordsLoaded(Handler h) {
193        mRecordsLoadedRegistrants.remove(h);
194    }
195
196    public void registerForImsiReady(Handler h, int what, Object obj) {
197        if (mDestroyed.get()) {
198            return;
199        }
200
201        Registrant r = new Registrant(h, what, obj);
202        mImsiReadyRegistrants.add(r);
203
204        if (mImsi != null) {
205            r.notifyRegistrant(new AsyncResult(null, null, null));
206        }
207    }
208    public void unregisterForImsiReady(Handler h) {
209        mImsiReadyRegistrants.remove(h);
210    }
211
212    public void registerForRecordsEvents(Handler h, int what, Object obj) {
213        Registrant r = new Registrant (h, what, obj);
214        mRecordsEventsRegistrants.add(r);
215
216        /* Notify registrant of all the possible events. This is to make sure registrant is
217        notified even if event occurred in the past. */
218        r.notifyResult(EVENT_MWI);
219        r.notifyResult(EVENT_CFI);
220    }
221    public void unregisterForRecordsEvents(Handler h) {
222        mRecordsEventsRegistrants.remove(h);
223    }
224
225    public void registerForNewSms(Handler h, int what, Object obj) {
226        Registrant r = new Registrant (h, what, obj);
227        mNewSmsRegistrants.add(r);
228    }
229    public void unregisterForNewSms(Handler h) {
230        mNewSmsRegistrants.remove(h);
231    }
232
233    public void registerForNetworkSelectionModeAutomatic(
234            Handler h, int what, Object obj) {
235        Registrant r = new Registrant (h, what, obj);
236        mNetworkSelectionModeAutomaticRegistrants.add(r);
237    }
238    public void unregisterForNetworkSelectionModeAutomatic(Handler h) {
239        mNetworkSelectionModeAutomaticRegistrants.remove(h);
240    }
241
242    /**
243     * Get the International Mobile Subscriber ID (IMSI) on a SIM
244     * for GSM, UMTS and like networks. Default is null if IMSI is
245     * not supported or unavailable.
246     *
247     * @return null if SIM is not yet ready or unavailable
248     */
249    public String getIMSI() {
250        return null;
251    }
252
253    /**
254     * Imsi could be set by ServiceStateTrackers in case of cdma
255     * @param imsi
256     */
257    public void setImsi(String imsi) {
258        mImsi = imsi;
259        mImsiReadyRegistrants.notifyRegistrants();
260    }
261
262    /**
263     * Get the Network Access ID (NAI) on a CSIM for CDMA like networks. Default is null if IMSI is
264     * not supported or unavailable.
265     *
266     * @return null if NAI is not yet ready or unavailable
267     */
268    public String getNAI() {
269        return null;
270    }
271
272    public String getMsisdnNumber() {
273        return mMsisdn;
274    }
275
276    /**
277     * Get the Group Identifier Level 1 (GID1) on a SIM for GSM.
278     * @return null if SIM is not yet ready
279     */
280    public String getGid1() {
281        return null;
282    }
283
284    /**
285     * Set subscriber number to SIM record
286     *
287     * The subscriber number is stored in EF_MSISDN (TS 51.011)
288     *
289     * When the operation is complete, onComplete will be sent to its handler
290     *
291     * @param alphaTag alpha-tagging of the dailing nubmer (up to 10 characters)
292     * @param number dailing nubmer (up to 20 digits)
293     *        if the number starts with '+', then set to international TOA
294     * @param onComplete
295     *        onComplete.obj will be an AsyncResult
296     *        ((AsyncResult)onComplete.obj).exception == null on success
297     *        ((AsyncResult)onComplete.obj).exception != null on fail
298     */
299    public void setMsisdnNumber(String alphaTag, String number,
300            Message onComplete) {
301
302        mMsisdn = number;
303        mMsisdnTag = alphaTag;
304
305        if (DBG) log("Set MSISDN: " + mMsisdnTag +" " + mMsisdn);
306
307
308        AdnRecord adn = new AdnRecord(mMsisdnTag, mMsisdn);
309
310        new AdnRecordLoader(mFh).updateEF(adn, EF_MSISDN, EF_EXT1, 1, null,
311                obtainMessage(EVENT_SET_MSISDN_DONE, onComplete));
312    }
313
314    public String getMsisdnAlphaTag() {
315        return mMsisdnTag;
316    }
317
318    public String getVoiceMailNumber() {
319        return mVoiceMailNum;
320    }
321
322    /**
323     * Return Service Provider Name stored in SIM (EF_SPN=0x6F46) or in RUIM (EF_RUIM_SPN=0x6F41).
324     *
325     * @return null if SIM is not yet ready or no RUIM entry
326     */
327    public String getServiceProviderName() {
328        String providerName = mSpn;
329
330        // Check for null pointers, mParentApp can be null after dispose,
331        // which did occur after removing a SIM.
332        UiccCardApplication parentApp = mParentApp;
333        if (parentApp != null) {
334            UiccCard card = parentApp.getUiccCard();
335            if (card != null) {
336                String brandOverride = card.getOperatorBrandOverride();
337                if (brandOverride != null) {
338                    log("getServiceProviderName: override");
339                    providerName = brandOverride;
340                } else {
341                    log("getServiceProviderName: no brandOverride");
342                }
343            } else {
344                log("getServiceProviderName: card is null");
345            }
346        } else {
347            log("getServiceProviderName: mParentApp is null");
348        }
349        log("getServiceProviderName: providerName=" + providerName);
350        return providerName;
351    }
352
353    protected void setServiceProviderName(String spn) {
354        mSpn = spn;
355    }
356
357    /**
358     * Set voice mail number to SIM record
359     *
360     * The voice mail number can be stored either in EF_MBDN (TS 51.011) or
361     * EF_MAILBOX_CPHS (CPHS 4.2)
362     *
363     * If EF_MBDN is available, store the voice mail number to EF_MBDN
364     *
365     * If EF_MAILBOX_CPHS is enabled, store the voice mail number to EF_CHPS
366     *
367     * So the voice mail number will be stored in both EFs if both are available
368     *
369     * Return error only if both EF_MBDN and EF_MAILBOX_CPHS fail.
370     *
371     * When the operation is complete, onComplete will be sent to its handler
372     *
373     * @param alphaTag alpha-tagging of the dailing nubmer (upto 10 characters)
374     * @param voiceNumber dailing nubmer (upto 20 digits)
375     *        if the number is start with '+', then set to international TOA
376     * @param onComplete
377     *        onComplete.obj will be an AsyncResult
378     *        ((AsyncResult)onComplete.obj).exception == null on success
379     *        ((AsyncResult)onComplete.obj).exception != null on fail
380     */
381    public abstract void setVoiceMailNumber(String alphaTag, String voiceNumber,
382            Message onComplete);
383
384    public String getVoiceMailAlphaTag() {
385        return mVoiceMailTag;
386    }
387
388    /**
389     * Sets the SIM voice message waiting indicator records
390     * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported
391     * @param countWaiting The number of messages waiting, if known. Use
392     *                     -1 to indicate that an unknown number of
393     *                      messages are waiting
394     */
395    public abstract void setVoiceMessageWaiting(int line, int countWaiting);
396
397    /**
398     * Called by GsmPhone to update VoiceMail count
399     */
400    public abstract int getVoiceMessageCount();
401
402    /**
403     * Called by STK Service when REFRESH is received.
404     * @param fileChanged indicates whether any files changed
405     * @param fileList if non-null, a list of EF files that changed
406     */
407    public abstract void onRefresh(boolean fileChanged, int[] fileList);
408
409    /**
410     * Called by subclasses (SimRecords and RuimRecords) whenever
411     * IccRefreshResponse.REFRESH_RESULT_INIT event received
412     */
413    protected void onIccRefreshInit() {
414        mAdnCache.reset();
415        UiccCardApplication parentApp = mParentApp;
416        if ((parentApp != null) &&
417                (parentApp.getState() == AppState.APPSTATE_READY)) {
418            // This will cause files to be reread
419            sendMessage(obtainMessage(EVENT_APP_READY));
420        }
421    }
422
423    public boolean getRecordsLoaded() {
424        if (mRecordsToLoad == 0 && mRecordsRequested == true) {
425            return true;
426        } else {
427            return false;
428        }
429    }
430
431    //***** Overridden from Handler
432    @Override
433    public void handleMessage(Message msg) {
434        AsyncResult ar;
435
436        switch (msg.what) {
437            case EVENT_GET_ICC_RECORD_DONE:
438                try {
439                    ar = (AsyncResult) msg.obj;
440                    IccRecordLoaded recordLoaded = (IccRecordLoaded) ar.userObj;
441                    if (DBG) log(recordLoaded.getEfName() + " LOADED");
442
443                    if (ar.exception != null) {
444                        loge("Record Load Exception: " + ar.exception);
445                    } else {
446                        recordLoaded.onRecordLoaded(ar);
447                    }
448                }catch (RuntimeException exc) {
449                    // I don't want these exceptions to be fatal
450                    loge("Exception parsing SIM record: " + exc);
451                } finally {
452                    // Count up record load responses even if they are fails
453                    onRecordLoaded();
454                }
455                break;
456
457            case EVENT_AKA_AUTHENTICATE_DONE:
458                ar = (AsyncResult)msg.obj;
459                auth_rsp = null;
460                if (DBG) log("EVENT_AKA_AUTHENTICATE_DONE");
461                if (ar.exception != null) {
462                    loge("Exception ICC SIM AKA: " + ar.exception);
463                } else {
464                    try {
465                        auth_rsp = (IccIoResult)ar.result;
466                        if (DBG) log("ICC SIM AKA: auth_rsp = " + auth_rsp);
467                    } catch (Exception e) {
468                        loge("Failed to parse ICC SIM AKA contents: " + e);
469                    }
470                }
471                synchronized (mLock) {
472                    mLock.notifyAll();
473                }
474
475                break;
476
477            default:
478                super.handleMessage(msg);
479        }
480    }
481
482    protected abstract void onRecordLoaded();
483
484    protected abstract void onAllRecordsLoaded();
485
486    /**
487     * Returns the SpnDisplayRule based on settings on the SIM and the
488     * specified plmn (currently-registered PLMN).  See TS 22.101 Annex A
489     * and TS 51.011 10.3.11 for details.
490     *
491     * If the SPN is not found on the SIM, the rule is always PLMN_ONLY.
492     * Generally used for GSM/UMTS and the like SIMs.
493     */
494    public abstract int getDisplayRule(String plmn);
495
496    /**
497     * Return true if "Restriction of menu options for manual PLMN selection"
498     * bit is set or EF_CSP data is unavailable, return false otherwise.
499     * Generally used for GSM/UMTS and the like SIMs.
500     */
501    public boolean isCspPlmnEnabled() {
502        return false;
503    }
504
505    /**
506     * Returns the 5 or 6 digit MCC/MNC of the operator that
507     * provided the SIM card. Returns null of SIM is not yet ready
508     * or is not valid for the type of IccCard. Generally used for
509     * GSM/UMTS and the like SIMS
510     */
511    public String getOperatorNumeric() {
512        return null;
513    }
514
515    /**
516     * Get the current Voice call forwarding flag for GSM/UMTS and the like SIMs
517     *
518     * @return true if enabled
519     */
520    public boolean getVoiceCallForwardingFlag() {
521        return false;
522    }
523
524    /**
525     * Set the voice call forwarding flag for GSM/UMTS and the like SIMs
526     *
527     * @param line to enable/disable
528     * @param enable
529     * @param number to which CFU is enabled
530     */
531    public void setVoiceCallForwardingFlag(int line, boolean enable, String number) {
532    }
533
534    /**
535     * Indicates wether SIM is in provisioned state or not.
536     * Overridden only if SIM can be dynamically provisioned via OTA.
537     *
538     * @return true if provisioned
539     */
540    public boolean isProvisioned () {
541        return true;
542    }
543
544    /**
545     * Write string to log file
546     *
547     * @param s is the string to write
548     */
549    protected abstract void log(String s);
550
551    /**
552     * Write error string to log file.
553     *
554     * @param s is the string to write
555     */
556    protected abstract void loge(String s);
557
558    /**
559     * Return an interface to retrieve the ISIM records for IMS, if available.
560     * @return the interface to retrieve the ISIM records, or null if not supported
561     */
562    public IsimRecords getIsimRecords() {
563        return null;
564    }
565
566    public UsimServiceTable getUsimServiceTable() {
567        return null;
568    }
569
570    protected void setSystemProperty(String key, String val) {
571        TelephonyManager.getDefault().setTelephonyProperty(mParentApp.getPhoneId(), key, val);
572
573        log("[key, value]=" + key + ", " +  val);
574    }
575
576    /**
577     * Returns the response of the SIM application on the UICC to authentication
578     * challenge/response algorithm. The data string and challenge response are
579     * Base64 encoded Strings.
580     * Can support EAP-SIM, EAP-AKA with results encoded per 3GPP TS 31.102.
581     *
582     * @param authContext parameter P2 that specifies the authentication context per 3GPP TS 31.102 (Section 7.1.2)
583     * @param data authentication challenge data
584     * @return challenge response
585     */
586    public String getIccSimChallengeResponse(int authContext, String data) {
587        if (DBG) log("getIccSimChallengeResponse:");
588
589        try {
590            synchronized(mLock) {
591                CommandsInterface ci = mCi;
592                UiccCardApplication parentApp = mParentApp;
593                if (ci != null && parentApp != null) {
594                    ci.requestIccSimAuthentication(authContext, data,
595                            parentApp.getAid(),
596                            obtainMessage(EVENT_AKA_AUTHENTICATE_DONE));
597                    try {
598                        mLock.wait();
599                    } catch (InterruptedException e) {
600                        loge("getIccSimChallengeResponse: Fail, interrupted"
601                                + " while trying to request Icc Sim Auth");
602                        return null;
603                    }
604                } else {
605                    loge( "getIccSimChallengeResponse: "
606                            + "Fail, ci or parentApp is null");
607                    return null;
608                }
609            }
610        } catch(Exception e) {
611            loge( "getIccSimChallengeResponse: "
612                    + "Fail while trying to request Icc Sim Auth");
613            return null;
614        }
615
616        if (DBG) log("getIccSimChallengeResponse: return auth_rsp");
617
618        return android.util.Base64.encodeToString(auth_rsp.payload, android.util.Base64.NO_WRAP);
619    }
620
621    protected boolean requirePowerOffOnSimRefreshReset() {
622        return mContext.getResources().getBoolean(
623            com.android.internal.R.bool.config_requireRadioPowerOffOnSimRefreshReset);
624    }
625
626    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
627        pw.println("IccRecords: " + this);
628        pw.println(" mDestroyed=" + mDestroyed);
629        pw.println(" mCi=" + mCi);
630        pw.println(" mFh=" + mFh);
631        pw.println(" mParentApp=" + mParentApp);
632        pw.println(" recordsLoadedRegistrants: size=" + mRecordsLoadedRegistrants.size());
633        for (int i = 0; i < mRecordsLoadedRegistrants.size(); i++) {
634            pw.println("  recordsLoadedRegistrants[" + i + "]="
635                    + ((Registrant)mRecordsLoadedRegistrants.get(i)).getHandler());
636        }
637        pw.println(" mImsiReadyRegistrants: size=" + mImsiReadyRegistrants.size());
638        for (int i = 0; i < mImsiReadyRegistrants.size(); i++) {
639            pw.println("  mImsiReadyRegistrants[" + i + "]="
640                    + ((Registrant)mImsiReadyRegistrants.get(i)).getHandler());
641        }
642        pw.println(" mRecordsEventsRegistrants: size=" + mRecordsEventsRegistrants.size());
643        for (int i = 0; i < mRecordsEventsRegistrants.size(); i++) {
644            pw.println("  mRecordsEventsRegistrants[" + i + "]="
645                    + ((Registrant)mRecordsEventsRegistrants.get(i)).getHandler());
646        }
647        pw.println(" mNewSmsRegistrants: size=" + mNewSmsRegistrants.size());
648        for (int i = 0; i < mNewSmsRegistrants.size(); i++) {
649            pw.println("  mNewSmsRegistrants[" + i + "]="
650                    + ((Registrant)mNewSmsRegistrants.get(i)).getHandler());
651        }
652        pw.println(" mNetworkSelectionModeAutomaticRegistrants: size="
653                + mNetworkSelectionModeAutomaticRegistrants.size());
654        for (int i = 0; i < mNetworkSelectionModeAutomaticRegistrants.size(); i++) {
655            pw.println("  mNetworkSelectionModeAutomaticRegistrants[" + i + "]="
656                    + ((Registrant)mNetworkSelectionModeAutomaticRegistrants.get(i)).getHandler());
657        }
658        pw.println(" mRecordsRequested=" + mRecordsRequested);
659        pw.println(" mRecordsToLoad=" + mRecordsToLoad);
660        pw.println(" mRdnCache=" + mAdnCache);
661        pw.println(" iccid=" + mIccId);
662        pw.println(" mMsisdn=" + mMsisdn);
663        pw.println(" mMsisdnTag=" + mMsisdnTag);
664        pw.println(" mVoiceMailNum=" + mVoiceMailNum);
665        pw.println(" mVoiceMailTag=" + mVoiceMailTag);
666        pw.println(" mNewVoiceMailNum=" + mNewVoiceMailNum);
667        pw.println(" mNewVoiceMailTag=" + mNewVoiceMailTag);
668        pw.println(" mIsVoiceMailFixed=" + mIsVoiceMailFixed);
669        pw.println(" mImsi=" + mImsi);
670        pw.println(" mMncLength=" + mMncLength);
671        pw.println(" mMailboxIndex=" + mMailboxIndex);
672        pw.println(" mSpn=" + mSpn);
673        pw.flush();
674    }
675}
676