RuimRecords.java revision 0764b3df0fdd465c260fc2180b0243194e7b1f31
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                    mSpn = GsmAlphabet.gsm7BitPackedToString(spnData, 0, (numBytes*8)/7);
309                    break;
310                case UserData.ENCODING_7BIT_ASCII:
311                    mSpn =  new String(spnData, 0, numBytes, "US-ASCII");
312                break;
313                case UserData.ENCODING_UNICODE_16:
314                    mSpn =  new String(spnData, 0, numBytes, "utf-16");
315                    break;
316                default:
317                    log("SPN encoding not supported");
318                }
319            } catch(Exception e) {
320                log("spn decode error: " + e);
321            }
322            if (DBG) log("spn=" + mSpn);
323            if (DBG) log("spnCondition=" + mCsimSpnDisplayCondition);
324            SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn);
325        }
326    }
327
328    private class EfCsimMdnLoaded implements IccRecordLoaded {
329        @Override
330        public String getEfName() {
331            return "EF_CSIM_MDN";
332        }
333
334        @Override
335        public void onRecordLoaded(AsyncResult ar) {
336            byte[] data = (byte[]) ar.result;
337            if (DBG) log("CSIM_MDN=" + IccUtils.bytesToHexString(data));
338            // Refer to C.S0065 5.2.35
339            int mdnDigitsNum = 0x0F & data[0];
340            mMdn = IccUtils.cdmaBcdToString(data, 1, mdnDigitsNum);
341            if (DBG) log("CSIM MDN=" + mMdn);
342        }
343    }
344
345    private class EfCsimImsimLoaded implements IccRecordLoaded {
346        @Override
347        public String getEfName() {
348            return "EF_CSIM_IMSIM";
349        }
350
351        @Override
352        public void onRecordLoaded(AsyncResult ar) {
353            byte[] data = (byte[]) ar.result;
354            if (DBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data));
355            // C.S0065 section 5.2.2 for IMSI_M encoding
356            // C.S0005 section 2.3.1 for MIN encoding in IMSI_M.
357            boolean provisioned = ((data[7] & 0x80) == 0x80);
358
359            if (provisioned) {
360                int first3digits = ((0x03 & data[2]) << 8) + (0xFF & data[1]);
361                int second3digits = (((0xFF & data[5]) << 8) | (0xFF & data[4])) >> 6;
362                int digit7 = 0x0F & (data[4] >> 2);
363                if (digit7 > 0x09) digit7 = 0;
364                int last3digits = ((0x03 & data[4]) << 8) | (0xFF & data[3]);
365                first3digits = adjstMinDigits(first3digits);
366                second3digits = adjstMinDigits(second3digits);
367                last3digits = adjstMinDigits(last3digits);
368
369                StringBuilder builder = new StringBuilder();
370                builder.append(String.format(Locale.US, "%03d", first3digits));
371                builder.append(String.format(Locale.US, "%03d", second3digits));
372                builder.append(String.format(Locale.US, "%d", digit7));
373                builder.append(String.format(Locale.US, "%03d", last3digits));
374                mMin = builder.toString();
375                if (DBG) log("min present=" + mMin);
376            } else {
377                if (DBG) log("min not present");
378            }
379        }
380    }
381
382    private class EfCsimCdmaHomeLoaded implements IccRecordLoaded {
383        @Override
384        public String getEfName() {
385            return "EF_CSIM_CDMAHOME";
386        }
387
388        @Override
389        public void onRecordLoaded(AsyncResult ar) {
390            // Per C.S0065 section 5.2.8
391            ArrayList<byte[]> dataList = (ArrayList<byte[]>) ar.result;
392            if (DBG) log("CSIM_CDMAHOME data size=" + dataList.size());
393            if (dataList.isEmpty()) {
394                return;
395            }
396            StringBuilder sidBuf = new StringBuilder();
397            StringBuilder nidBuf = new StringBuilder();
398
399            for (byte[] data : dataList) {
400                if (data.length == 5) {
401                    int sid = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF);
402                    int nid = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF);
403                    sidBuf.append(sid).append(',');
404                    nidBuf.append(nid).append(',');
405                }
406            }
407            // remove trailing ","
408            sidBuf.setLength(sidBuf.length()-1);
409            nidBuf.setLength(nidBuf.length()-1);
410
411            mHomeSystemId = sidBuf.toString();
412            mHomeNetworkId = nidBuf.toString();
413        }
414    }
415
416    private class EfCsimEprlLoaded implements IccRecordLoaded {
417        @Override
418        public String getEfName() {
419            return "EF_CSIM_EPRL";
420        }
421        @Override
422        public void onRecordLoaded(AsyncResult ar) {
423            onGetCSimEprlDone(ar);
424        }
425    }
426
427    private void onGetCSimEprlDone(AsyncResult ar) {
428        // C.S0065 section 5.2.57 for EFeprl encoding
429        // C.S0016 section 3.5.5 for PRL format.
430        byte[] data = (byte[]) ar.result;
431        if (DBG) log("CSIM_EPRL=" + IccUtils.bytesToHexString(data));
432
433        // Only need the first 4 bytes of record
434        if (data.length > 3) {
435            int prlId = ((data[2] & 0xFF) << 8) | (data[3] & 0xFF);
436            mPrlVersion = Integer.toString(prlId);
437        }
438        if (DBG) log("CSIM PRL version=" + mPrlVersion);
439    }
440
441    @Override
442    public void handleMessage(Message msg) {
443        AsyncResult ar;
444
445        byte data[];
446
447        boolean isRecordLoadResponse = false;
448
449        if (mDestroyed.get()) {
450            loge("Received message " + msg +
451                    "[" + msg.what + "] while being destroyed. Ignoring.");
452            return;
453        }
454
455        try { switch (msg.what) {
456            case EVENT_APP_READY:
457                onReady();
458                break;
459
460            case EVENT_GET_DEVICE_IDENTITY_DONE:
461                log("Event EVENT_GET_DEVICE_IDENTITY_DONE Received");
462            break;
463
464            /* IO events */
465            case EVENT_GET_IMSI_DONE:
466                isRecordLoadResponse = true;
467
468                ar = (AsyncResult)msg.obj;
469                if (ar.exception != null) {
470                    loge("Exception querying IMSI, Exception:" + ar.exception);
471                    break;
472                }
473
474                mImsi = (String) ar.result;
475
476                // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more
477                // than 15 (and usually 15).
478                if (mImsi != null && (mImsi.length() < 6 || mImsi.length() > 15)) {
479                    loge("invalid IMSI " + mImsi);
480                    mImsi = null;
481                }
482
483                log("IMSI: " + mImsi.substring(0, 6) + "xxxxxxxxx");
484
485                String operatorNumeric = getRUIMOperatorNumeric();
486                if (operatorNumeric != null) {
487                    if(operatorNumeric.length() <= 6){
488                        MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false);
489                    }
490                }
491            break;
492
493            case EVENT_GET_CDMA_SUBSCRIPTION_DONE:
494                ar = (AsyncResult)msg.obj;
495                String localTemp[] = (String[])ar.result;
496                if (ar.exception != null) {
497                    break;
498                }
499
500                mMyMobileNumber = localTemp[0];
501                mMin2Min1 = localTemp[3];
502                mPrlVersion = localTemp[4];
503
504                log("MDN: " + mMyMobileNumber + " MIN: " + mMin2Min1);
505
506            break;
507
508            case EVENT_GET_ICCID_DONE:
509                isRecordLoadResponse = true;
510
511                ar = (AsyncResult)msg.obj;
512                data = (byte[])ar.result;
513
514                if (ar.exception != null) {
515                    break;
516                }
517
518                mIccId = IccUtils.bcdToString(data, 0, data.length);
519
520                log("iccid: " + mIccId);
521
522            break;
523
524            case EVENT_UPDATE_DONE:
525                ar = (AsyncResult)msg.obj;
526                if (ar.exception != null) {
527                    Rlog.i(LOG_TAG, "RuimRecords update failed", ar.exception);
528                }
529            break;
530
531            case EVENT_GET_ALL_SMS_DONE:
532            case EVENT_MARK_SMS_READ_DONE:
533            case EVENT_SMS_ON_RUIM:
534            case EVENT_GET_SMS_DONE:
535                Rlog.w(LOG_TAG, "Event not supported: " + msg.what);
536                break;
537
538            // TODO: probably EF_CST should be read instead
539            case EVENT_GET_SST_DONE:
540                log("Event EVENT_GET_SST_DONE Received");
541            break;
542
543            case EVENT_RUIM_REFRESH:
544                isRecordLoadResponse = false;
545                ar = (AsyncResult)msg.obj;
546                if (ar.exception == null) {
547                    handleRuimRefresh((IccRefreshResponse)ar.result);
548                }
549                break;
550
551            default:
552                super.handleMessage(msg);   // IccRecords handles generic record load responses
553
554        }}catch (RuntimeException exc) {
555            // I don't want these exceptions to be fatal
556            Rlog.w(LOG_TAG, "Exception parsing RUIM record", exc);
557        } finally {
558            // Count up record load responses even if they are fails
559            if (isRecordLoadResponse) {
560                onRecordLoaded();
561            }
562        }
563    }
564
565    private String findBestLanguage(byte[] languages) {
566        String bestMatch = null;
567        String[] locales = mContext.getAssets().getLocales();
568
569        if ((languages == null) || (locales == null)) return null;
570
571        // Each 2-bytes consists of one language
572        for (int i = 0; (i + 1) < languages.length; i += 2) {
573            try {
574                String lang = new String(languages, i, 2, "ISO-8859-1");
575                for (int j = 0; j < locales.length; j++) {
576                    if (locales[j] != null && locales[j].length() >= 2 &&
577                        locales[j].substring(0, 2).equals(lang)) {
578                        return lang;
579                    }
580                }
581                if (bestMatch != null) break;
582            } catch(java.io.UnsupportedEncodingException e) {
583                log ("Failed to parse SIM language records");
584            }
585        }
586        // no match found. return null
587        return null;
588    }
589
590    private void setLocaleFromCsim() {
591        String prefLang = null;
592        // check EFli then EFpl
593        prefLang = findBestLanguage(mEFli);
594
595        if (prefLang == null) {
596            prefLang = findBestLanguage(mEFpl);
597        }
598
599        if (prefLang != null) {
600            // check country code from SIM
601            String imsi = getIMSI();
602            String country = null;
603            if (imsi != null) {
604                country = MccTable.countryCodeForMcc(
605                                    Integer.parseInt(imsi.substring(0,3)));
606            }
607            log("Setting locale to " + prefLang + "_" + country);
608            MccTable.setSystemLocale(mContext, prefLang, country);
609        } else {
610            log ("No suitable CSIM selected locale");
611        }
612    }
613
614    @Override
615    protected void onRecordLoaded() {
616        // One record loaded successfully or failed, In either case
617        // we need to update the recordsToLoad count
618        mRecordsToLoad -= 1;
619        if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested);
620
621        if (mRecordsToLoad == 0 && mRecordsRequested == true) {
622            onAllRecordsLoaded();
623        } else if (mRecordsToLoad < 0) {
624            loge("recordsToLoad <0, programmer error suspected");
625            mRecordsToLoad = 0;
626        }
627    }
628
629    @Override
630    protected void onAllRecordsLoaded() {
631        if (DBG) log("record load complete");
632
633        // Further records that can be inserted are Operator/OEM dependent
634
635        String operator = getRUIMOperatorNumeric();
636        if (!TextUtils.isEmpty(operator)) {
637            log("onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" +
638                    operator + "'");
639            SystemProperties.set(PROPERTY_ICC_OPERATOR_NUMERIC, operator);
640        } else {
641            log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping");
642        }
643
644        if (!TextUtils.isEmpty(mImsi)) {
645            log("onAllRecordsLoaded set mcc imsi=" + mImsi);
646            SystemProperties.set(PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
647                    MccTable.countryCodeForMcc(Integer.parseInt(mImsi.substring(0,3))));
648        } else {
649            log("onAllRecordsLoaded empty imsi skipping setting mcc");
650        }
651
652        setLocaleFromCsim();
653        mRecordsLoadedRegistrants.notifyRegistrants(
654            new AsyncResult(null, null, null));
655    }
656
657    @Override
658    public void onReady() {
659        fetchRuimRecords();
660
661        mCi.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE));
662    }
663
664
665    private void fetchRuimRecords() {
666        mRecordsRequested = true;
667
668        if (DBG) log("fetchRuimRecords " + mRecordsToLoad);
669
670        mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));
671        mRecordsToLoad++;
672
673        mFh.loadEFTransparent(EF_ICCID,
674                obtainMessage(EVENT_GET_ICCID_DONE));
675        mRecordsToLoad++;
676
677        mFh.loadEFTransparent(EF_PL,
678                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfPlLoaded()));
679        mRecordsToLoad++;
680
681        mFh.loadEFTransparent(EF_CSIM_LI,
682                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimLiLoaded()));
683        mRecordsToLoad++;
684
685        mFh.loadEFTransparent(EF_CSIM_SPN,
686                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimSpnLoaded()));
687        mRecordsToLoad++;
688
689        mFh.loadEFLinearFixed(EF_CSIM_MDN, 1,
690                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimMdnLoaded()));
691        mRecordsToLoad++;
692
693        mFh.loadEFTransparent(EF_CSIM_IMSIM,
694                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimImsimLoaded()));
695        mRecordsToLoad++;
696
697        mFh.loadEFLinearFixedAll(EF_CSIM_CDMAHOME,
698                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimCdmaHomeLoaded()));
699        mRecordsToLoad++;
700
701        // Entire PRL could be huge. We are only interested in
702        // the first 4 bytes of the record.
703        mFh.loadEFTransparent(EF_CSIM_EPRL, 4,
704                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimEprlLoaded()));
705        mRecordsToLoad++;
706
707        if (DBG) log("fetchRuimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested);
708        // Further records that can be inserted are Operator/OEM dependent
709    }
710
711    /**
712     * {@inheritDoc}
713     *
714     * No Display rule for RUIMs yet.
715     */
716    @Override
717    public int getDisplayRule(String plmn) {
718        // TODO together with spn
719        return 0;
720    }
721
722    @Override
723    public boolean isProvisioned() {
724        // If UICC card has CSIM app, look for MDN and MIN field
725        // to determine if the SIM is provisioned.  Otherwise,
726        // consider the SIM is provisioned. (for case of ordinal
727        // USIM only UICC.)
728        // If PROPERTY_TEST_CSIM is defined, bypess provision check
729        // and consider the SIM is provisioned.
730        if (SystemProperties.getBoolean(PROPERTY_TEST_CSIM, false)) {
731            return true;
732        }
733
734        if (mParentApp == null) {
735            return false;
736        }
737
738        if (mParentApp.getType() == AppType.APPTYPE_CSIM &&
739            ((mMdn == null) || (mMin == null))) {
740            return false;
741        }
742        return true;
743    }
744
745    @Override
746    public void setVoiceMessageWaiting(int line, int countWaiting) {
747        if (line != 1) {
748            // only profile 1 is supported
749            return;
750        }
751
752        // range check
753        if (countWaiting < 0) {
754            countWaiting = -1;
755        } else if (countWaiting > 0xff) {
756            // C.S0015-B v2, 4.5.12
757            // range: 0-99
758            countWaiting = 0xff;
759        }
760        mCountVoiceMessages = countWaiting;
761
762        mRecordsEventsRegistrants.notifyResult(EVENT_MWI);
763    }
764
765    private void handleRuimRefresh(IccRefreshResponse refreshResponse) {
766        if (refreshResponse == null) {
767            if (DBG) log("handleRuimRefresh received without input");
768            return;
769        }
770
771        if (refreshResponse.aid != null &&
772                !refreshResponse.aid.equals(mParentApp.getAid())) {
773            // This is for different app. Ignore.
774            return;
775        }
776
777        switch (refreshResponse.refreshResult) {
778            case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE:
779                if (DBG) log("handleRuimRefresh with SIM_REFRESH_FILE_UPDATED");
780                mAdnCache.reset();
781                fetchRuimRecords();
782                break;
783            case IccRefreshResponse.REFRESH_RESULT_INIT:
784                if (DBG) log("handleRuimRefresh with SIM_REFRESH_INIT");
785                // need to reload all files (that we care about)
786                onIccRefreshInit();
787                break;
788            case IccRefreshResponse.REFRESH_RESULT_RESET:
789                if (DBG) log("handleRuimRefresh with SIM_REFRESH_RESET");
790                mCi.setRadioPower(false, null);
791                /* Note: no need to call setRadioPower(true).  Assuming the desired
792                * radio power state is still ON (as tracked by ServiceStateTracker),
793                * ServiceStateTracker will call setRadioPower when it receives the
794                * RADIO_STATE_CHANGED notification for the power off.  And if the
795                * desired power state has changed in the interim, we don't want to
796                * override it with an unconditional power on.
797                */
798                break;
799            default:
800                // unknown refresh operation
801                if (DBG) log("handleRuimRefresh with unknown operation");
802                break;
803        }
804    }
805
806    public String getMdn() {
807        return mMdn;
808    }
809
810    public String getMin() {
811        return mMin;
812    }
813
814    public String getSid() {
815        return mHomeSystemId;
816    }
817
818    public String getNid() {
819        return mHomeNetworkId;
820    }
821
822    public boolean getCsimSpnDisplayCondition() {
823        return mCsimSpnDisplayCondition;
824    }
825    @Override
826    protected void log(String s) {
827        Rlog.d(LOG_TAG, "[RuimRecords] " + s);
828    }
829
830    @Override
831    protected void loge(String s) {
832        Rlog.e(LOG_TAG, "[RuimRecords] " + s);
833    }
834
835    @Override
836    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
837        pw.println("RuimRecords: " + this);
838        pw.println(" extends:");
839        super.dump(fd, pw, args);
840        pw.println(" mOtaCommited=" + mOtaCommited);
841        pw.println(" mMyMobileNumber=" + mMyMobileNumber);
842        pw.println(" mMin2Min1=" + mMin2Min1);
843        pw.println(" mPrlVersion=" + mPrlVersion);
844        pw.println(" mEFpl[]=" + Arrays.toString(mEFpl));
845        pw.println(" mEFli[]=" + Arrays.toString(mEFli));
846        pw.println(" mCsimSpnDisplayCondition=" + mCsimSpnDisplayCondition);
847        pw.println(" mMdn=" + mMdn);
848        pw.println(" mMin=" + mMin);
849        pw.println(" mHomeSystemId=" + mHomeSystemId);
850        pw.println(" mHomeNetworkId=" + mHomeNetworkId);
851        pw.flush();
852    }
853}
854