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