RuimRecords.java revision 64bfd98578babdd437f1a83d2d5e1fc92c76e729
1/*
2 * Copyright (C) 2008 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
19
20import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
21import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
22
23import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
24import static com.android.internal.telephony.TelephonyProperties.PROPERTY_TEST_CSIM;
25
26import java.io.FileDescriptor;
27import java.io.PrintWriter;
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.Locale;
31import android.content.Context;
32import android.os.AsyncResult;
33import android.os.Handler;
34import android.os.Message;
35import android.os.Registrant;
36import android.os.SystemProperties;
37import android.telephony.Rlog;
38
39import com.android.internal.telephony.CommandsInterface;
40import com.android.internal.telephony.IccCardConstants;
41import com.android.internal.telephony.GsmAlphabet;
42import com.android.internal.telephony.PhoneBase;
43import com.android.internal.telephony.TelephonyProperties;
44import com.android.internal.telephony.MccTable;
45
46// can't be used since VoiceMailConstants is not public
47//import com.android.internal.telephony.gsm.VoiceMailConstants;
48import com.android.internal.telephony.PhoneProxy;
49import com.android.internal.telephony.cdma.sms.UserData;
50import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
51
52
53/**
54 * {@hide}
55 */
56public final class RuimRecords extends IccRecords {
57    static final String LOG_TAG = "CDMA";
58
59    private static final boolean DBG = true;
60    private boolean  m_ota_commited=false;
61
62    // ***** Instance Variables
63
64    private String mMyMobileNumber;
65    private String mMin2Min1;
66
67    private String mPrlVersion;
68    // From CSIM application
69    private byte[] mEFpl = null;
70    private byte[] mEFli = null;
71    boolean mCsimSpnDisplayCondition = false;
72    private String mMdn;
73    private String mMin;
74    private String mHomeSystemId;
75    private String mHomeNetworkId;
76
77    // ***** Event Constants
78    private static final int EVENT_GET_IMSI_DONE = 3;
79    private static final int EVENT_GET_DEVICE_IDENTITY_DONE = 4;
80    private static final int EVENT_GET_ICCID_DONE = 5;
81    private static final int EVENT_GET_CDMA_SUBSCRIPTION_DONE = 10;
82    private static final int EVENT_UPDATE_DONE = 14;
83    private static final int EVENT_GET_SST_DONE = 17;
84    private static final int EVENT_GET_ALL_SMS_DONE = 18;
85    private static final int EVENT_MARK_SMS_READ_DONE = 19;
86
87    private static final int EVENT_SMS_ON_RUIM = 21;
88    private static final int EVENT_GET_SMS_DONE = 22;
89
90    private static final int EVENT_RUIM_REFRESH = 31;
91
92    public RuimRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
93        super(app, c, ci);
94
95        adnCache = new AdnRecordCache(mFh);
96
97        recordsRequested = false;  // No load request is made till SIM ready
98
99        // recordsToLoad is set to 0 because no requests are made yet
100        recordsToLoad = 0;
101
102        // NOTE the EVENT_SMS_ON_RUIM is not registered
103        mCi.registerForIccRefresh(this, EVENT_RUIM_REFRESH, null);
104
105        // Start off by setting empty state
106        resetRecords();
107
108        mParentApp.registerForReady(this, EVENT_APP_READY, null);
109    }
110
111    @Override
112    public void dispose() {
113        if (DBG) log("Disposing RuimRecords " + this);
114        //Unregister for all events
115        mCi.unregisterForIccRefresh(this);
116        mParentApp.unregisterForReady(this);
117        resetRecords();
118        super.dispose();
119    }
120
121    @Override
122    protected void finalize() {
123        if(DBG) log("RuimRecords finalized");
124    }
125
126    protected void resetRecords() {
127        countVoiceMessages = 0;
128        mncLength = UNINITIALIZED;
129        iccid = null;
130
131        adnCache.reset();
132
133        // Don't clean up PROPERTY_ICC_OPERATOR_ISO_COUNTRY and
134        // PROPERTY_ICC_OPERATOR_NUMERIC here. Since not all CDMA
135        // devices have RUIM, these properties should keep the original
136        // values, e.g. build time settings, when there is no RUIM but
137        // set new values when RUIM is available and loaded.
138
139        // recordsRequested is set to false indicating that the SIM
140        // read requests made so far are not valid. This is set to
141        // true only when fresh set of read requests are made.
142        recordsRequested = false;
143    }
144
145    @Override
146    public String getIMSI() {
147        return mImsi;
148    }
149
150    public String getMdnNumber() {
151        return mMyMobileNumber;
152    }
153
154    public String getCdmaMin() {
155         return mMin2Min1;
156    }
157
158    /** Returns null if RUIM is not yet ready */
159    public String getPrlVersion() {
160        return mPrlVersion;
161    }
162
163    @Override
164    public void setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete){
165        // In CDMA this is Operator/OEM dependent
166        AsyncResult.forMessage((onComplete)).exception =
167                new IccException("setVoiceMailNumber not implemented");
168        onComplete.sendToTarget();
169        loge("method setVoiceMailNumber is not implemented");
170    }
171
172    /**
173     * Called by CCAT Service when REFRESH is received.
174     * @param fileChanged indicates whether any files changed
175     * @param fileList if non-null, a list of EF files that changed
176     */
177    @Override
178    public void onRefresh(boolean fileChanged, int[] fileList) {
179        if (fileChanged) {
180            // A future optimization would be to inspect fileList and
181            // only reload those files that we care about.  For now,
182            // just re-fetch all RUIM records that we cache.
183            fetchRuimRecords();
184        }
185    }
186
187    private int adjstMinDigits (int digits) {
188        // Per C.S0005 section 2.3.1.
189        digits += 111;
190        digits = (digits % 10 == 0)?(digits - 10):digits;
191        digits = ((digits / 10) % 10 == 0)?(digits - 100):digits;
192        digits = ((digits / 100) % 10 == 0)?(digits - 1000):digits;
193        return digits;
194    }
195
196    /**
197     * Returns the 5 or 6 digit MCC/MNC of the operator that
198     *  provided the RUIM card. Returns null of RUIM is not yet ready
199     */
200    public String getRUIMOperatorNumeric() {
201        if (mImsi == null) {
202            return null;
203        }
204
205        if (mncLength != UNINITIALIZED && mncLength != UNKNOWN) {
206            // Length = length of MCC + length of MNC
207            // length of mcc = 3 (3GPP2 C.S0005 - Section 2.3)
208            return mImsi.substring(0, 3 + mncLength);
209        }
210
211        // Guess the MNC length based on the MCC if we don't
212        // have a valid value in ef[ad]
213
214        int mcc = Integer.parseInt(mImsi.substring(0,3));
215        return mImsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc));
216    }
217
218    // Refer to ETSI TS 102.221
219    private class EfPlLoaded implements IccRecordLoaded {
220        public String getEfName() {
221            return "EF_PL";
222        }
223
224        public void onRecordLoaded(AsyncResult ar) {
225            mEFpl = (byte[]) ar.result;
226            if (DBG) log("EF_PL=" + IccUtils.bytesToHexString(mEFpl));
227        }
228    }
229
230    // Refer to C.S0065 5.2.26
231    private class EfCsimLiLoaded implements IccRecordLoaded {
232        public String getEfName() {
233            return "EF_CSIM_LI";
234        }
235
236        public void onRecordLoaded(AsyncResult ar) {
237            mEFli = (byte[]) ar.result;
238            // convert csim efli data to iso 639 format
239            for (int i = 0; i < mEFli.length; i+=2) {
240                switch(mEFli[i+1]) {
241                case 0x01: mEFli[i] = 'e'; mEFli[i+1] = 'n';break;
242                case 0x02: mEFli[i] = 'f'; mEFli[i+1] = 'r';break;
243                case 0x03: mEFli[i] = 'e'; mEFli[i+1] = 's';break;
244                case 0x04: mEFli[i] = 'j'; mEFli[i+1] = 'a';break;
245                case 0x05: mEFli[i] = 'k'; mEFli[i+1] = 'o';break;
246                case 0x06: mEFli[i] = 'z'; mEFli[i+1] = 'h';break;
247                case 0x07: mEFli[i] = 'h'; mEFli[i+1] = 'e';break;
248                default: mEFli[i] = ' '; mEFli[i+1] = ' ';
249                }
250            }
251
252            if (DBG) log("EF_LI=" + IccUtils.bytesToHexString(mEFli));
253        }
254    }
255
256    // Refer to C.S0065 5.2.32
257    private class EfCsimSpnLoaded implements IccRecordLoaded {
258        public String getEfName() {
259            return "EF_CSIM_SPN";
260        }
261
262        public void onRecordLoaded(AsyncResult ar) {
263            byte[] data = (byte[]) ar.result;
264            if (DBG) log("CSIM_SPN=" +
265                         IccUtils.bytesToHexString(data));
266
267            // C.S0065 for EF_SPN decoding
268            mCsimSpnDisplayCondition = ((0x01 & data[0]) != 0);
269
270            int encoding = data[1];
271            int language = data[2];
272            byte[] spnData = new byte[32];
273            int len = ((data.length - 3) < 32) ? (data.length - 3) : 32;
274            System.arraycopy(data, 3, spnData, 0, len);
275
276            int numBytes;
277            for (numBytes = 0; numBytes < spnData.length; numBytes++) {
278                if ((spnData[numBytes] & 0xFF) == 0xFF) break;
279            }
280
281            if (numBytes == 0) {
282                spn = "";
283                return;
284            }
285            try {
286                switch (encoding) {
287                case UserData.ENCODING_OCTET:
288                case UserData.ENCODING_LATIN:
289                    spn = new String(spnData, 0, numBytes, "ISO-8859-1");
290                    break;
291                case UserData.ENCODING_IA5:
292                case UserData.ENCODING_GSM_7BIT_ALPHABET:
293                case UserData.ENCODING_7BIT_ASCII:
294                    spn = GsmAlphabet.gsm7BitPackedToString(spnData, 0, (numBytes*8)/7);
295                    break;
296                case UserData.ENCODING_UNICODE_16:
297                    spn =  new String(spnData, 0, numBytes, "utf-16");
298                    break;
299                default:
300                    log("SPN encoding not supported");
301                }
302            } catch(Exception e) {
303                log("spn decode error: " + e);
304            }
305            if (DBG) log("spn=" + spn);
306            if (DBG) log("spnCondition=" + mCsimSpnDisplayCondition);
307            SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, spn);
308        }
309    }
310
311    private class EfCsimMdnLoaded implements IccRecordLoaded {
312        public String getEfName() {
313            return "EF_CSIM_MDN";
314        }
315
316        public void onRecordLoaded(AsyncResult ar) {
317            byte[] data = (byte[]) ar.result;
318            if (DBG) log("CSIM_MDN=" + IccUtils.bytesToHexString(data));
319            // Refer to C.S0065 5.2.35
320            int mdnDigitsNum = 0x0F & data[0];
321            mMdn = IccUtils.cdmaBcdToString(data, 1, mdnDigitsNum);
322            if (DBG) log("CSIM MDN=" + mMdn);
323        }
324    }
325
326    private class EfCsimImsimLoaded implements IccRecordLoaded {
327        public String getEfName() {
328            return "EF_CSIM_IMSIM";
329        }
330
331        public void onRecordLoaded(AsyncResult ar) {
332            byte[] data = (byte[]) ar.result;
333            if (DBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data));
334            // C.S0065 section 5.2.2 for IMSI_M encoding
335            // C.S0005 section 2.3.1 for MIN encoding in IMSI_M.
336            boolean provisioned = ((data[7] & 0x80) == 0x80);
337
338            if (provisioned) {
339                int first3digits = ((0x03 & data[2]) << 8) + (0xFF & data[1]);
340                int second3digits = (((0xFF & data[5]) << 8) | (0xFF & data[4])) >> 6;
341                int digit7 = 0x0F & (data[4] >> 2);
342                if (digit7 > 0x09) digit7 = 0;
343                int last3digits = ((0x03 & data[4]) << 8) | (0xFF & data[3]);
344                first3digits = adjstMinDigits(first3digits);
345                second3digits = adjstMinDigits(second3digits);
346                last3digits = adjstMinDigits(last3digits);
347
348                StringBuilder builder = new StringBuilder();
349                builder.append(String.format(Locale.US, "%03d", first3digits));
350                builder.append(String.format(Locale.US, "%03d", second3digits));
351                builder.append(String.format(Locale.US, "%d", digit7));
352                builder.append(String.format(Locale.US, "%03d", last3digits));
353                mMin = builder.toString();
354                if (DBG) log("min present=" + mMin);
355            } else {
356                if (DBG) log("min not present");
357            }
358        }
359    }
360
361    private class EfCsimCdmaHomeLoaded implements IccRecordLoaded {
362        public String getEfName() {
363            return "EF_CSIM_CDMAHOME";
364        }
365
366        public void onRecordLoaded(AsyncResult ar) {
367            // Per C.S0065 section 5.2.8
368            ArrayList<byte[]> dataList = (ArrayList<byte[]>) ar.result;
369            if (DBG) log("CSIM_CDMAHOME data size=" + dataList.size());
370            if (dataList.isEmpty()) {
371                return;
372            }
373            StringBuilder sidBuf = new StringBuilder();
374            StringBuilder nidBuf = new StringBuilder();
375
376            for (byte[] data : dataList) {
377                if (data.length == 5) {
378                    int sid = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF);
379                    int nid = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF);
380                    sidBuf.append(sid).append(',');
381                    nidBuf.append(nid).append(',');
382                }
383            }
384            // remove trailing ","
385            sidBuf.setLength(sidBuf.length()-1);
386            nidBuf.setLength(nidBuf.length()-1);
387
388            mHomeSystemId = sidBuf.toString();
389            mHomeNetworkId = nidBuf.toString();
390        }
391    }
392
393    private class EfCsimEprlLoaded implements IccRecordLoaded {
394        public String getEfName() {
395            return "EF_CSIM_EPRL";
396        }
397        public void onRecordLoaded(AsyncResult ar) {
398            onGetCSimEprlDone(ar);
399        }
400    }
401
402    private void onGetCSimEprlDone(AsyncResult ar) {
403        // C.S0065 section 5.2.57 for EFeprl encoding
404        // C.S0016 section 3.5.5 for PRL format.
405        byte[] data = (byte[]) ar.result;
406        if (DBG) log("CSIM_EPRL=" + IccUtils.bytesToHexString(data));
407
408        // Only need the first 4 bytes of record
409        if (data.length > 3) {
410            int prlId = ((data[2] & 0xFF) << 8) | (data[3] & 0xFF);
411            mPrlVersion = Integer.toString(prlId);
412        }
413        if (DBG) log("CSIM PRL version=" + mPrlVersion);
414    }
415
416    @Override
417    public void handleMessage(Message msg) {
418        AsyncResult ar;
419
420        byte data[];
421
422        boolean isRecordLoadResponse = false;
423
424        if (mDestroyed.get()) {
425            loge("Received message " + msg +
426                    "[" + msg.what + "] while being destroyed. Ignoring.");
427            return;
428        }
429
430        try { switch (msg.what) {
431            case EVENT_APP_READY:
432                onReady();
433                break;
434
435            case EVENT_GET_DEVICE_IDENTITY_DONE:
436                log("Event EVENT_GET_DEVICE_IDENTITY_DONE Received");
437            break;
438
439            /* IO events */
440            case EVENT_GET_IMSI_DONE:
441                isRecordLoadResponse = true;
442
443                ar = (AsyncResult)msg.obj;
444                if (ar.exception != null) {
445                    loge("Exception querying IMSI, Exception:" + ar.exception);
446                    break;
447                }
448
449                mImsi = (String) ar.result;
450
451                // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more
452                // than 15 (and usually 15).
453                if (mImsi != null && (mImsi.length() < 6 || mImsi.length() > 15)) {
454                    loge("invalid IMSI " + mImsi);
455                    mImsi = null;
456                }
457
458                log("IMSI: " + mImsi.substring(0, 6) + "xxxxxxxxx");
459
460                String operatorNumeric = getRUIMOperatorNumeric();
461                if (operatorNumeric != null) {
462                    if(operatorNumeric.length() <= 6){
463                        MccTable.updateMccMncConfiguration(mContext, operatorNumeric);
464                    }
465                }
466            break;
467
468            case EVENT_GET_CDMA_SUBSCRIPTION_DONE:
469                ar = (AsyncResult)msg.obj;
470                String localTemp[] = (String[])ar.result;
471                if (ar.exception != null) {
472                    break;
473                }
474
475                mMyMobileNumber = localTemp[0];
476                mMin2Min1 = localTemp[3];
477                mPrlVersion = localTemp[4];
478
479                log("MDN: " + mMyMobileNumber + " MIN: " + mMin2Min1);
480
481            break;
482
483            case EVENT_GET_ICCID_DONE:
484                isRecordLoadResponse = true;
485
486                ar = (AsyncResult)msg.obj;
487                data = (byte[])ar.result;
488
489                if (ar.exception != null) {
490                    break;
491                }
492
493                iccid = IccUtils.bcdToString(data, 0, data.length);
494
495                log("iccid: " + iccid);
496
497            break;
498
499            case EVENT_UPDATE_DONE:
500                ar = (AsyncResult)msg.obj;
501                if (ar.exception != null) {
502                    Rlog.i(LOG_TAG, "RuimRecords update failed", ar.exception);
503                }
504            break;
505
506            case EVENT_GET_ALL_SMS_DONE:
507            case EVENT_MARK_SMS_READ_DONE:
508            case EVENT_SMS_ON_RUIM:
509            case EVENT_GET_SMS_DONE:
510                Rlog.w(LOG_TAG, "Event not supported: " + msg.what);
511                break;
512
513            // TODO: probably EF_CST should be read instead
514            case EVENT_GET_SST_DONE:
515                log("Event EVENT_GET_SST_DONE Received");
516            break;
517
518            case EVENT_RUIM_REFRESH:
519                isRecordLoadResponse = false;
520                ar = (AsyncResult)msg.obj;
521                if (ar.exception == null) {
522                    handleRuimRefresh((IccRefreshResponse)ar.result);
523                }
524                break;
525
526            default:
527                super.handleMessage(msg);   // IccRecords handles generic record load responses
528
529        }}catch (RuntimeException exc) {
530            // I don't want these exceptions to be fatal
531            Rlog.w(LOG_TAG, "Exception parsing RUIM record", exc);
532        } finally {
533            // Count up record load responses even if they are fails
534            if (isRecordLoadResponse) {
535                onRecordLoaded();
536            }
537        }
538    }
539
540    private String findBestLanguage(byte[] languages) {
541        String bestMatch = null;
542        String[] locales = mContext.getAssets().getLocales();
543
544        if ((languages == null) || (locales == null)) return null;
545
546        // Each 2-bytes consists of one language
547        for (int i = 0; (i + 1) < languages.length; i += 2) {
548            try {
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).equals(lang)) {
553                        return lang;
554                    }
555                }
556                if (bestMatch != null) break;
557            } catch(java.io.UnsupportedEncodingException e) {
558                log ("Failed to parse SIM language records");
559            }
560        }
561        // no match found. return null
562        return null;
563    }
564
565    private void setLocaleFromCsim() {
566        String prefLang = null;
567        // check EFli then EFpl
568        prefLang = findBestLanguage(mEFli);
569
570        if (prefLang == null) {
571            prefLang = findBestLanguage(mEFpl);
572        }
573
574        if (prefLang != null) {
575            // check country code from SIM
576            String imsi = getIMSI();
577            String country = null;
578            if (imsi != null) {
579                country = MccTable.countryCodeForMcc(
580                                    Integer.parseInt(imsi.substring(0,3)));
581            }
582            log("Setting locale to " + prefLang + "_" + country);
583            MccTable.setSystemLocale(mContext, prefLang, country);
584        } else {
585            log ("No suitable CSIM selected locale");
586        }
587    }
588
589    @Override
590    protected void onRecordLoaded() {
591        // One record loaded successfully or failed, In either case
592        // we need to update the recordsToLoad count
593        recordsToLoad -= 1;
594        if (DBG) log("onRecordLoaded " + recordsToLoad + " requested: " + recordsRequested);
595
596        if (recordsToLoad == 0 && recordsRequested == true) {
597            onAllRecordsLoaded();
598        } else if (recordsToLoad < 0) {
599            loge("recordsToLoad <0, programmer error suspected");
600            recordsToLoad = 0;
601        }
602    }
603
604    @Override
605    protected void onAllRecordsLoaded() {
606        if (DBG) log("record load complete");
607
608        // Further records that can be inserted are Operator/OEM dependent
609
610        String operator = getRUIMOperatorNumeric();
611        log("RuimRecords: onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" +
612                operator + "'");
613        SystemProperties.set(PROPERTY_ICC_OPERATOR_NUMERIC, operator);
614
615        if (mImsi != null) {
616            SystemProperties.set(PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
617                    MccTable.countryCodeForMcc(Integer.parseInt(mImsi.substring(0,3))));
618        }
619
620        setLocaleFromCsim();
621        recordsLoadedRegistrants.notifyRegistrants(
622            new AsyncResult(null, null, null));
623    }
624
625    @Override
626    public void onReady() {
627        fetchRuimRecords();
628
629        mCi.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE));
630    }
631
632
633    private void fetchRuimRecords() {
634        recordsRequested = true;
635
636        if (DBG) log("fetchRuimRecords " + recordsToLoad);
637
638        mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));
639        recordsToLoad++;
640
641        mFh.loadEFTransparent(EF_ICCID,
642                obtainMessage(EVENT_GET_ICCID_DONE));
643        recordsToLoad++;
644
645        mFh.loadEFTransparent(EF_PL,
646                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfPlLoaded()));
647        recordsToLoad++;
648
649        mFh.loadEFTransparent(EF_CSIM_LI,
650                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimLiLoaded()));
651        recordsToLoad++;
652
653        mFh.loadEFTransparent(EF_CSIM_SPN,
654                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimSpnLoaded()));
655        recordsToLoad++;
656
657        mFh.loadEFLinearFixed(EF_CSIM_MDN, 1,
658                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimMdnLoaded()));
659        recordsToLoad++;
660
661        mFh.loadEFTransparent(EF_CSIM_IMSIM,
662                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimImsimLoaded()));
663        recordsToLoad++;
664
665        mFh.loadEFLinearFixedAll(EF_CSIM_CDMAHOME,
666                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimCdmaHomeLoaded()));
667        recordsToLoad++;
668
669        // Entire PRL could be huge. We are only interested in
670        // the first 4 bytes of the record.
671        mFh.loadEFTransparent(EF_CSIM_EPRL, 4,
672                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimEprlLoaded()));
673        recordsToLoad++;
674
675        if (DBG) log("fetchRuimRecords " + recordsToLoad + " requested: " + recordsRequested);
676        // Further records that can be inserted are Operator/OEM dependent
677    }
678
679    /**
680     * {@inheritDoc}
681     *
682     * No Display rule for RUIMs yet.
683     */
684    @Override
685    public int getDisplayRule(String plmn) {
686        // TODO together with spn
687        return 0;
688    }
689
690    @Override
691    public boolean isProvisioned() {
692        // If UICC card has CSIM app, look for MDN and MIN field
693        // to determine if the SIM is provisioned.  Otherwise,
694        // consider the SIM is provisioned. (for case of ordinal
695        // USIM only UICC.)
696        // If PROPERTY_TEST_CSIM is defined, bypess provision check
697        // and consider the SIM is provisioned.
698        if (SystemProperties.getBoolean(PROPERTY_TEST_CSIM, false)) {
699            return true;
700        }
701
702        if (mParentApp == null) {
703            return false;
704        }
705
706        if (mParentApp.getType() == AppType.APPTYPE_CSIM &&
707            ((mMdn == null) || (mMin == null))) {
708            return false;
709        }
710        return true;
711    }
712
713    @Override
714    public void setVoiceMessageWaiting(int line, int countWaiting) {
715        if (line != 1) {
716            // only profile 1 is supported
717            return;
718        }
719
720        // range check
721        if (countWaiting < 0) {
722            countWaiting = -1;
723        } else if (countWaiting > 0xff) {
724            // C.S0015-B v2, 4.5.12
725            // range: 0-99
726            countWaiting = 0xff;
727        }
728        countVoiceMessages = countWaiting;
729
730        mRecordsEventsRegistrants.notifyResult(EVENT_MWI);
731    }
732
733    private void handleRuimRefresh(IccRefreshResponse refreshResponse) {
734        if (refreshResponse == null) {
735            if (DBG) log("handleRuimRefresh received without input");
736            return;
737        }
738
739        if (refreshResponse.aid != null &&
740                !refreshResponse.aid.equals(mParentApp.getAid())) {
741            // This is for different app. Ignore.
742            return;
743        }
744
745        switch (refreshResponse.refreshResult) {
746            case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE:
747                if (DBG) log("handleRuimRefresh with SIM_REFRESH_FILE_UPDATED");
748                adnCache.reset();
749                fetchRuimRecords();
750                break;
751            case IccRefreshResponse.REFRESH_RESULT_INIT:
752                if (DBG) log("handleRuimRefresh with SIM_REFRESH_INIT");
753                // need to reload all files (that we care about)
754                onIccRefreshInit();
755                break;
756            case IccRefreshResponse.REFRESH_RESULT_RESET:
757                if (DBG) log("handleRuimRefresh with SIM_REFRESH_RESET");
758                mCi.setRadioPower(false, null);
759                /* Note: no need to call setRadioPower(true).  Assuming the desired
760                * radio power state is still ON (as tracked by ServiceStateTracker),
761                * ServiceStateTracker will call setRadioPower when it receives the
762                * RADIO_STATE_CHANGED notification for the power off.  And if the
763                * desired power state has changed in the interim, we don't want to
764                * override it with an unconditional power on.
765                */
766                break;
767            default:
768                // unknown refresh operation
769                if (DBG) log("handleRuimRefresh with unknown operation");
770                break;
771        }
772    }
773
774    public String getMdn() {
775        return mMdn;
776    }
777
778    public String getMin() {
779        return mMin;
780    }
781
782    public String getSid() {
783        return mHomeSystemId;
784    }
785
786    public String getNid() {
787        return mHomeNetworkId;
788    }
789
790    public boolean getCsimSpnDisplayCondition() {
791        return mCsimSpnDisplayCondition;
792    }
793    @Override
794    protected void log(String s) {
795        Rlog.d(LOG_TAG, "[RuimRecords] " + s);
796    }
797
798    @Override
799    protected void loge(String s) {
800        Rlog.e(LOG_TAG, "[RuimRecords] " + s);
801    }
802
803    @Override
804    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
805        pw.println("RuimRecords: " + this);
806        pw.println(" extends:");
807        super.dump(fd, pw, args);
808        pw.println(" m_ota_commited=" + m_ota_commited);
809        pw.println(" mMyMobileNumber=" + mMyMobileNumber);
810        pw.println(" mMin2Min1=" + mMin2Min1);
811        pw.println(" mPrlVersion=" + mPrlVersion);
812        pw.println(" mEFpl[]=" + Arrays.toString(mEFpl));
813        pw.println(" mEFli[]=" + Arrays.toString(mEFli));
814        pw.println(" mCsimSpnDisplayCondition=" + mCsimSpnDisplayCondition);
815        pw.println(" mMdn=" + mMdn);
816        pw.println(" mMin=" + mMin);
817        pw.println(" mHomeSystemId=" + mHomeSystemId);
818        pw.println(" mHomeNetworkId=" + mHomeNetworkId);
819        pw.flush();
820    }
821}
822