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