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