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