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