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