RuimRecords.java revision 5a68da341137763920f682b8d239c9229b934a05
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
19import static com.android.internal.telephony.TelephonyProperties.PROPERTY_TEST_CSIM;
20
21import java.io.FileDescriptor;
22import java.io.PrintWriter;
23import java.util.ArrayList;
24import java.util.Arrays;
25import java.util.Locale;
26import android.content.Context;
27import android.os.AsyncResult;
28import android.os.Message;
29import android.os.SystemProperties;
30import android.telephony.SubscriptionManager;
31import android.telephony.Rlog;
32import android.text.TextUtils;
33import android.util.Log;
34import android.content.res.Resources;
35
36import com.android.internal.telephony.CommandsInterface;
37import com.android.internal.telephony.GsmAlphabet;
38import com.android.internal.telephony.MccTable;
39import com.android.internal.telephony.SubscriptionController;
40
41import com.android.internal.telephony.cdma.sms.UserData;
42import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
43import com.android.internal.util.BitwiseInputStream;
44
45/**
46 * {@hide}
47 */
48public final class RuimRecords extends IccRecords {
49    static final String LOG_TAG = "RuimRecords";
50
51    private boolean  mOtaCommited=false;
52
53    // ***** Instance Variables
54
55    private String mMyMobileNumber;
56    private String mMin2Min1;
57
58    private String mPrlVersion;
59    // From CSIM application
60    private byte[] mEFpl = null;
61    private byte[] mEFli = null;
62    boolean mCsimSpnDisplayCondition = false;
63    private String mMdn;
64    private String mMin;
65    private String mHomeSystemId;
66    private String mHomeNetworkId;
67    private String mNai;
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        mMncLength = UNINITIALIZED;
137        log("setting0 mMncLength" + mMncLength);
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    /** Returns null if RUIM is not yet ready */
174    public String getNAI() {
175        return mNai;
176    }
177
178    @Override
179    public void setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete){
180        // In CDMA this is Operator/OEM dependent
181        AsyncResult.forMessage((onComplete)).exception =
182                new IccException("setVoiceMailNumber not implemented");
183        onComplete.sendToTarget();
184        loge("method setVoiceMailNumber is not implemented");
185    }
186
187    /**
188     * Called by CCAT Service when REFRESH is received.
189     * @param fileChanged indicates whether any files changed
190     * @param fileList if non-null, a list of EF files that changed
191     */
192    @Override
193    public void onRefresh(boolean fileChanged, int[] fileList) {
194        if (fileChanged) {
195            // A future optimization would be to inspect fileList and
196            // only reload those files that we care about.  For now,
197            // just re-fetch all RUIM records that we cache.
198            fetchRuimRecords();
199        }
200    }
201
202    private int adjstMinDigits (int digits) {
203        // Per C.S0005 section 2.3.1.
204        digits += 111;
205        digits = (digits % 10 == 0)?(digits - 10):digits;
206        digits = ((digits / 10) % 10 == 0)?(digits - 100):digits;
207        digits = ((digits / 100) % 10 == 0)?(digits - 1000):digits;
208        return digits;
209    }
210
211    /**
212     * Returns the 5 or 6 digit MCC/MNC of the operator that
213     *  provided the RUIM card. Returns null of RUIM is not yet ready
214     */
215    public String getRUIMOperatorNumeric() {
216        if (mImsi == null) {
217            return null;
218        }
219
220        if (mMncLength != UNINITIALIZED && mMncLength != UNKNOWN) {
221            // Length = length of MCC + length of MNC
222            // length of mcc = 3 (3GPP2 C.S0005 - Section 2.3)
223            return mImsi.substring(0, 3 + mMncLength);
224        }
225
226        // Guess the MNC length based on the MCC if we don't
227        // have a valid value in ef[ad]
228
229        int mcc = Integer.parseInt(mImsi.substring(0,3));
230        return mImsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc));
231    }
232
233    // Refer to ETSI TS 102.221
234    private class EfPlLoaded implements IccRecordLoaded {
235        @Override
236        public String getEfName() {
237            return "EF_PL";
238        }
239
240        @Override
241        public void onRecordLoaded(AsyncResult ar) {
242            mEFpl = (byte[]) ar.result;
243            if (DBG) log("EF_PL=" + IccUtils.bytesToHexString(mEFpl));
244        }
245    }
246
247    // Refer to C.S0065 5.2.26
248    private class EfCsimLiLoaded implements IccRecordLoaded {
249        @Override
250        public String getEfName() {
251            return "EF_CSIM_LI";
252        }
253
254        @Override
255        public void onRecordLoaded(AsyncResult ar) {
256            mEFli = (byte[]) ar.result;
257            // convert csim efli data to iso 639 format
258            for (int i = 0; i < mEFli.length; i+=2) {
259                switch(mEFli[i+1]) {
260                case 0x01: mEFli[i] = 'e'; mEFli[i+1] = 'n';break;
261                case 0x02: mEFli[i] = 'f'; mEFli[i+1] = 'r';break;
262                case 0x03: mEFli[i] = 'e'; mEFli[i+1] = 's';break;
263                case 0x04: mEFli[i] = 'j'; mEFli[i+1] = 'a';break;
264                case 0x05: mEFli[i] = 'k'; mEFli[i+1] = 'o';break;
265                case 0x06: mEFli[i] = 'z'; mEFli[i+1] = 'h';break;
266                case 0x07: mEFli[i] = 'h'; mEFli[i+1] = 'e';break;
267                default: mEFli[i] = ' '; mEFli[i+1] = ' ';
268                }
269            }
270
271            if (DBG) log("EF_LI=" + IccUtils.bytesToHexString(mEFli));
272        }
273    }
274
275    // Refer to C.S0065 5.2.32
276    private class EfCsimSpnLoaded implements IccRecordLoaded {
277        @Override
278        public String getEfName() {
279            return "EF_CSIM_SPN";
280        }
281
282        @Override
283        public void onRecordLoaded(AsyncResult ar) {
284            byte[] data = (byte[]) ar.result;
285            if (DBG) log("CSIM_SPN=" +
286                         IccUtils.bytesToHexString(data));
287
288            // C.S0065 for EF_SPN decoding
289            mCsimSpnDisplayCondition = ((0x01 & data[0]) != 0);
290
291            int encoding = data[1];
292            int language = data[2];
293            byte[] spnData = new byte[32];
294            int len = ((data.length - 3) < 32) ? (data.length - 3) : 32;
295            System.arraycopy(data, 3, spnData, 0, len);
296
297            int numBytes;
298            for (numBytes = 0; numBytes < spnData.length; numBytes++) {
299                if ((spnData[numBytes] & 0xFF) == 0xFF) break;
300            }
301
302            if (numBytes == 0) {
303                setServiceProviderName("");
304                return;
305            }
306            try {
307                switch (encoding) {
308                case UserData.ENCODING_OCTET:
309                case UserData.ENCODING_LATIN:
310                    setServiceProviderName(new String(spnData, 0, numBytes, "ISO-8859-1"));
311                    break;
312                case UserData.ENCODING_IA5:
313                case UserData.ENCODING_GSM_7BIT_ALPHABET:
314                    setServiceProviderName(
315                            GsmAlphabet.gsm7BitPackedToString(spnData, 0, (numBytes*8)/7));
316                    break;
317                case UserData.ENCODING_7BIT_ASCII:
318                    String spn = new String(spnData, 0, numBytes, "US-ASCII");
319                    // To address issues with incorrect encoding scheme
320                    // programmed in some commercial CSIM cards, the decoded
321                    // SPN is checked to have characters in printable ASCII
322                    // range. If not, they are decoded with
323                    // ENCODING_GSM_7BIT_ALPHABET scheme.
324                    if (TextUtils.isPrintableAsciiOnly(spn)) {
325                        setServiceProviderName(spn);
326                    } else {
327                        if (DBG) log("Some corruption in SPN decoding = " + spn);
328                        if (DBG) log("Using ENCODING_GSM_7BIT_ALPHABET scheme...");
329                        setServiceProviderName(
330                                GsmAlphabet.gsm7BitPackedToString(spnData, 0, (numBytes * 8) / 7));
331                    }
332                break;
333                case UserData.ENCODING_UNICODE_16:
334                    setServiceProviderName(new String(spnData, 0, numBytes, "utf-16"));
335                    break;
336                default:
337                    log("SPN encoding not supported");
338                }
339            } catch(Exception e) {
340                log("spn decode error: " + e);
341            }
342            if (DBG) log("spn=" + getServiceProviderName());
343            if (DBG) log("spnCondition=" + mCsimSpnDisplayCondition);
344            mTelephonyManager.setSimOperatorNameForPhone(
345                    mParentApp.getPhoneId(), getServiceProviderName());
346        }
347    }
348
349    private class EfCsimMdnLoaded implements IccRecordLoaded {
350        @Override
351        public String getEfName() {
352            return "EF_CSIM_MDN";
353        }
354
355        @Override
356        public void onRecordLoaded(AsyncResult ar) {
357            byte[] data = (byte[]) ar.result;
358            if (DBG) log("CSIM_MDN=" + IccUtils.bytesToHexString(data));
359            // Refer to C.S0065 5.2.35
360            int mdnDigitsNum = 0x0F & data[0];
361            mMdn = IccUtils.cdmaBcdToString(data, 1, mdnDigitsNum);
362            if (DBG) log("CSIM MDN=" + mMdn);
363        }
364    }
365
366    private class EfCsimImsimLoaded implements IccRecordLoaded {
367        @Override
368        public String getEfName() {
369            return "EF_CSIM_IMSIM";
370        }
371
372        @Override
373        public void onRecordLoaded(AsyncResult ar) {
374            byte[] data = (byte[]) ar.result;
375            if (DBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data));
376            // C.S0065 section 5.2.2 for IMSI_M encoding
377            // C.S0005 section 2.3.1 for MIN encoding in IMSI_M.
378            boolean provisioned = ((data[7] & 0x80) == 0x80);
379
380            if (provisioned) {
381                int first3digits = ((0x03 & data[2]) << 8) + (0xFF & data[1]);
382                int second3digits = (((0xFF & data[5]) << 8) | (0xFF & data[4])) >> 6;
383                int digit7 = 0x0F & (data[4] >> 2);
384                if (digit7 > 0x09) digit7 = 0;
385                int last3digits = ((0x03 & data[4]) << 8) | (0xFF & data[3]);
386                first3digits = adjstMinDigits(first3digits);
387                second3digits = adjstMinDigits(second3digits);
388                last3digits = adjstMinDigits(last3digits);
389
390                StringBuilder builder = new StringBuilder();
391                builder.append(String.format(Locale.US, "%03d", first3digits));
392                builder.append(String.format(Locale.US, "%03d", second3digits));
393                builder.append(String.format(Locale.US, "%d", digit7));
394                builder.append(String.format(Locale.US, "%03d", last3digits));
395                mMin = builder.toString();
396                if (DBG) log("min present=" + mMin);
397            } else {
398                if (DBG) log("min not present");
399            }
400        }
401    }
402
403    private class EfCsimCdmaHomeLoaded implements IccRecordLoaded {
404        @Override
405        public String getEfName() {
406            return "EF_CSIM_CDMAHOME";
407        }
408
409        @Override
410        public void onRecordLoaded(AsyncResult ar) {
411            // Per C.S0065 section 5.2.8
412            ArrayList<byte[]> dataList = (ArrayList<byte[]>) ar.result;
413            if (DBG) log("CSIM_CDMAHOME data size=" + dataList.size());
414            if (dataList.isEmpty()) {
415                return;
416            }
417            StringBuilder sidBuf = new StringBuilder();
418            StringBuilder nidBuf = new StringBuilder();
419
420            for (byte[] data : dataList) {
421                if (data.length == 5) {
422                    int sid = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF);
423                    int nid = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF);
424                    sidBuf.append(sid).append(',');
425                    nidBuf.append(nid).append(',');
426                }
427            }
428            // remove trailing ","
429            sidBuf.setLength(sidBuf.length()-1);
430            nidBuf.setLength(nidBuf.length()-1);
431
432            mHomeSystemId = sidBuf.toString();
433            mHomeNetworkId = nidBuf.toString();
434        }
435    }
436
437    private class EfCsimEprlLoaded implements IccRecordLoaded {
438        @Override
439        public String getEfName() {
440            return "EF_CSIM_EPRL";
441        }
442        @Override
443        public void onRecordLoaded(AsyncResult ar) {
444            onGetCSimEprlDone(ar);
445        }
446    }
447
448    private void onGetCSimEprlDone(AsyncResult ar) {
449        // C.S0065 section 5.2.57 for EFeprl encoding
450        // C.S0016 section 3.5.5 for PRL format.
451        byte[] data = (byte[]) ar.result;
452        if (DBG) log("CSIM_EPRL=" + IccUtils.bytesToHexString(data));
453
454        // Only need the first 4 bytes of record
455        if (data.length > 3) {
456            int prlId = ((data[2] & 0xFF) << 8) | (data[3] & 0xFF);
457            mPrlVersion = Integer.toString(prlId);
458        }
459        if (DBG) log("CSIM PRL version=" + mPrlVersion);
460    }
461
462    private class EfCsimMipUppLoaded implements IccRecordLoaded {
463        @Override
464        public String getEfName() {
465            return "EF_CSIM_MIPUPP";
466        }
467
468        boolean checkLengthLegal(int length, int expectLength) {
469            if(length < expectLength) {
470                Log.e(LOG_TAG, "CSIM MIPUPP format error, length = " + length  +
471                        "expected length at least =" + expectLength);
472                return false;
473            } else {
474                return true;
475            }
476        }
477
478        @Override
479        public void onRecordLoaded(AsyncResult ar) {
480            // 3GPP2 C.S0065 section 5.2.24
481            byte[] data = (byte[]) ar.result;
482
483            if(data.length < 1) {
484                Log.e(LOG_TAG,"MIPUPP read error");
485                return;
486            }
487
488            BitwiseInputStream bitStream = new BitwiseInputStream(data);
489            try {
490                int  mipUppLength = bitStream.read(8);
491                //transfer length from byte to bit
492                mipUppLength = (mipUppLength << 3);
493
494                if (!checkLengthLegal(mipUppLength, 1)) {
495                    return;
496                }
497                //parse the MIPUPP body 3GPP2 C.S0016-C 3.5.8.6
498                int retryInfoInclude = bitStream.read(1);
499                mipUppLength--;
500
501                if(retryInfoInclude == 1) {
502                    if (!checkLengthLegal(mipUppLength, 11)) {
503                        return;
504                    }
505                    bitStream.skip(11); //not used now
506                    //transfer length from byte to bit
507                    mipUppLength -= 11;
508                }
509
510                if (!checkLengthLegal(mipUppLength, 4)) {
511                    return;
512                }
513                int numNai = bitStream.read(4);
514                mipUppLength -= 4;
515
516                //start parse NAI body
517                for(int index = 0; index < numNai; index++) {
518                    if (!checkLengthLegal(mipUppLength, 4)) {
519                        return;
520                    }
521                    int naiEntryIndex = bitStream.read(4);
522                    mipUppLength -= 4;
523
524                    if (!checkLengthLegal(mipUppLength, 8)) {
525                        return;
526                    }
527                    int naiLength = bitStream.read(8);
528                    mipUppLength -= 8;
529
530                    if(naiEntryIndex == 0) {
531                        //we find the one!
532                        if (!checkLengthLegal(mipUppLength, naiLength << 3)) {
533                            return;
534                        }
535                        char naiCharArray[] = new char[naiLength];
536                        for(int index1 = 0; index1 < naiLength; index1++) {
537                            naiCharArray[index1] = (char)(bitStream.read(8) & 0xFF);
538                        }
539                        mNai =  new String(naiCharArray);
540                        if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
541                            Log.v(LOG_TAG,"MIPUPP Nai = " + mNai);
542                        }
543                        return; //need not parsing further
544                    } else {
545                        //ignore this NAI body
546                        if (!checkLengthLegal(mipUppLength, (naiLength << 3) + 102)) {
547                            return;
548                        }
549                        bitStream.skip((naiLength << 3) + 101);//not used
550                        int mnAaaSpiIndicator = bitStream.read(1);
551                        mipUppLength -= ((naiLength << 3) + 102);
552
553                        if(mnAaaSpiIndicator == 1) {
554                            if (!checkLengthLegal(mipUppLength, 32)) {
555                                return;
556                            }
557                            bitStream.skip(32); //not used
558                            mipUppLength -= 32;
559                        }
560
561                        //MN-HA_AUTH_ALGORITHM
562                        if (!checkLengthLegal(mipUppLength, 5)) {
563                            return;
564                        }
565                        bitStream.skip(4);
566                        mipUppLength -= 4;
567                        int mnHaSpiIndicator = bitStream.read(1);
568                        mipUppLength--;
569
570                        if(mnHaSpiIndicator == 1) {
571                            if (!checkLengthLegal(mipUppLength, 32)) {
572                                return;
573                            }
574                            bitStream.skip(32);
575                            mipUppLength -= 32;
576                        }
577                    }
578                }
579            } catch(Exception e) {
580              Log.e(LOG_TAG,"MIPUPP read Exception error!");
581                return;
582            }
583        }
584    }
585
586    @Override
587    public void handleMessage(Message msg) {
588        AsyncResult ar;
589
590        byte data[];
591
592        boolean isRecordLoadResponse = false;
593
594        if (mDestroyed.get()) {
595            loge("Received message " + msg +
596                    "[" + msg.what + "] while being destroyed. Ignoring.");
597            return;
598        }
599
600        try { switch (msg.what) {
601            case EVENT_APP_READY:
602                onReady();
603                break;
604
605            case EVENT_GET_DEVICE_IDENTITY_DONE:
606                log("Event EVENT_GET_DEVICE_IDENTITY_DONE Received");
607            break;
608
609            /* IO events */
610            case EVENT_GET_IMSI_DONE:
611                isRecordLoadResponse = true;
612
613                ar = (AsyncResult)msg.obj;
614                if (ar.exception != null) {
615                    loge("Exception querying IMSI, Exception:" + ar.exception);
616                    break;
617                }
618
619                mImsi = (String) ar.result;
620
621                // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more
622                // than 15 (and usually 15).
623                if (mImsi != null && (mImsi.length() < 6 || mImsi.length() > 15)) {
624                    loge("invalid IMSI " + mImsi);
625                    mImsi = null;
626                }
627
628                // FIXME: CSIM IMSI may not contain the MNC.
629                if (false) {
630                    log("IMSI: " + mImsi.substring(0, 6) + "xxxxxxxxx");
631
632                    String operatorNumeric = getRUIMOperatorNumeric();
633                    if (operatorNumeric != null) {
634                        if (operatorNumeric.length() <= 6) {
635                            log("update mccmnc=" + operatorNumeric);
636                            MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false);
637                        }
638                    }
639                } else {
640                    String operatorNumeric = getRUIMOperatorNumeric();
641                    log("NO update mccmnc=" + operatorNumeric);
642                }
643
644            break;
645
646            case EVENT_GET_CDMA_SUBSCRIPTION_DONE:
647                ar = (AsyncResult)msg.obj;
648                String localTemp[] = (String[])ar.result;
649                if (ar.exception != null) {
650                    break;
651                }
652
653                mMyMobileNumber = localTemp[0];
654                mMin2Min1 = localTemp[3];
655                mPrlVersion = localTemp[4];
656
657                log("MDN: " + mMyMobileNumber + " MIN: " + mMin2Min1);
658
659            break;
660
661            case EVENT_GET_ICCID_DONE:
662                isRecordLoadResponse = true;
663
664                ar = (AsyncResult)msg.obj;
665                data = (byte[])ar.result;
666
667                if (ar.exception != null) {
668                    break;
669                }
670
671                mIccId = IccUtils.bcdToString(data, 0, data.length);
672
673                log("iccid: " + mIccId);
674
675            break;
676
677            case EVENT_UPDATE_DONE:
678                ar = (AsyncResult)msg.obj;
679                if (ar.exception != null) {
680                    Rlog.i(LOG_TAG, "RuimRecords update failed", ar.exception);
681                }
682            break;
683
684            case EVENT_GET_ALL_SMS_DONE:
685            case EVENT_MARK_SMS_READ_DONE:
686            case EVENT_SMS_ON_RUIM:
687            case EVENT_GET_SMS_DONE:
688                Rlog.w(LOG_TAG, "Event not supported: " + msg.what);
689                break;
690
691            // TODO: probably EF_CST should be read instead
692            case EVENT_GET_SST_DONE:
693                log("Event EVENT_GET_SST_DONE Received");
694            break;
695
696            case EVENT_RUIM_REFRESH:
697                isRecordLoadResponse = false;
698                ar = (AsyncResult)msg.obj;
699                if (ar.exception == null) {
700                    handleRuimRefresh((IccRefreshResponse)ar.result);
701                }
702                break;
703
704            default:
705                super.handleMessage(msg);   // IccRecords handles generic record load responses
706
707        }}catch (RuntimeException exc) {
708            // I don't want these exceptions to be fatal
709            Rlog.w(LOG_TAG, "Exception parsing RUIM record", exc);
710        } finally {
711            // Count up record load responses even if they are fails
712            if (isRecordLoadResponse) {
713                onRecordLoaded();
714            }
715        }
716    }
717
718    /**
719     * Returns an array of languages we have assets for.
720     *
721     * NOTE: This array will have duplicates. If this method will be caused
722     * frequently or in a tight loop, it can be rewritten for efficiency.
723     */
724    private static String[] getAssetLanguages(Context ctx) {
725        final String[] locales = ctx.getAssets().getLocales();
726        final String[] localeLangs = new String[locales.length];
727        for (int i = 0; i < locales.length; ++i) {
728            final String localeStr = locales[i];
729            final int separator = localeStr.indexOf('-');
730            if (separator < 0) {
731                localeLangs[i] = localeStr;
732            } else {
733                localeLangs[i] = localeStr.substring(0, separator);
734            }
735        }
736
737        return localeLangs;
738    }
739
740    @Override
741    protected void onRecordLoaded() {
742        // One record loaded successfully or failed, In either case
743        // we need to update the recordsToLoad count
744        mRecordsToLoad -= 1;
745        if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested);
746
747        if (mRecordsToLoad == 0 && mRecordsRequested == true) {
748            onAllRecordsLoaded();
749        } else if (mRecordsToLoad < 0) {
750            loge("recordsToLoad <0, programmer error suspected");
751            mRecordsToLoad = 0;
752        }
753    }
754
755    @Override
756    protected void onAllRecordsLoaded() {
757        if (DBG) log("record load complete");
758
759        // Further records that can be inserted are Operator/OEM dependent
760
761        // FIXME: CSIM IMSI may not contain the MNC.
762        if (false) {
763            String operator = getRUIMOperatorNumeric();
764            if (!TextUtils.isEmpty(operator)) {
765                log("onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" +
766                        operator + "'");
767                log("update icc_operator_numeric=" + operator);
768                mTelephonyManager.setSimOperatorNumericForPhone(
769                        mParentApp.getPhoneId(), operator);
770            } else {
771                log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping");
772            }
773
774            if (!TextUtils.isEmpty(mImsi)) {
775                log("onAllRecordsLoaded set mcc imsi=" + (VDBG ? ("=" + mImsi) : ""));
776                mTelephonyManager.setSimCountryIsoForPhone(
777                        mParentApp.getPhoneId(),
778                        MccTable.countryCodeForMcc(
779                        Integer.parseInt(mImsi.substring(0,3))));
780            } else {
781                log("onAllRecordsLoaded empty imsi skipping setting mcc");
782            }
783        }
784
785        setSimLanguage(mEFli, mEFpl);
786        mRecordsLoadedRegistrants.notifyRegistrants(
787            new AsyncResult(null, null, null));
788
789        // TODO: The below is hacky since the SubscriptionController may not be ready at this time.
790        if (!TextUtils.isEmpty(mMdn)) {
791            int phoneId = mParentApp.getUiccCard().getPhoneId();
792            int[] subIds = SubscriptionController.getInstance().getSubId(phoneId);
793            if (subIds != null) {
794                SubscriptionManager.from(mContext).setDisplayNumber(mMdn, subIds[0]);
795            } else {
796                log("Cannot call setDisplayNumber: invalid subId");
797            }
798        }
799    }
800
801    @Override
802    public void onReady() {
803        fetchRuimRecords();
804
805        mCi.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE));
806    }
807
808
809    private void fetchRuimRecords() {
810        mRecordsRequested = true;
811
812        if (DBG) log("fetchRuimRecords " + mRecordsToLoad);
813
814        mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));
815        mRecordsToLoad++;
816
817        mFh.loadEFTransparent(EF_ICCID,
818                obtainMessage(EVENT_GET_ICCID_DONE));
819        mRecordsToLoad++;
820
821        Resources resource = Resources.getSystem();
822        if (resource.getBoolean(com.android.internal.R.bool.config_use_sim_language_file)) {
823            mFh.loadEFTransparent(EF_PL,
824                    obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfPlLoaded()));
825            mRecordsToLoad++;
826
827            mFh.loadEFTransparent(EF_CSIM_LI,
828                    obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimLiLoaded()));
829            mRecordsToLoad++;
830        }
831
832        mFh.loadEFTransparent(EF_CSIM_SPN,
833                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimSpnLoaded()));
834        mRecordsToLoad++;
835
836        mFh.loadEFLinearFixed(EF_CSIM_MDN, 1,
837                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimMdnLoaded()));
838        mRecordsToLoad++;
839
840        mFh.loadEFTransparent(EF_CSIM_IMSIM,
841                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimImsimLoaded()));
842        mRecordsToLoad++;
843
844        mFh.loadEFLinearFixedAll(EF_CSIM_CDMAHOME,
845                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimCdmaHomeLoaded()));
846        mRecordsToLoad++;
847
848        // Entire PRL could be huge. We are only interested in
849        // the first 4 bytes of the record.
850        mFh.loadEFTransparent(EF_CSIM_EPRL, 4,
851                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimEprlLoaded()));
852        mRecordsToLoad++;
853
854        mFh.loadEFTransparent(EF_CSIM_MIPUPP,
855                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimMipUppLoaded()));
856        mRecordsToLoad++;
857
858        if (DBG) log("fetchRuimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested);
859        // Further records that can be inserted are Operator/OEM dependent
860    }
861
862    /**
863     * {@inheritDoc}
864     *
865     * No Display rule for RUIMs yet.
866     */
867    @Override
868    public int getDisplayRule(String plmn) {
869        // TODO together with spn
870        return 0;
871    }
872
873    @Override
874    public boolean isProvisioned() {
875        // If UICC card has CSIM app, look for MDN and MIN field
876        // to determine if the SIM is provisioned.  Otherwise,
877        // consider the SIM is provisioned. (for case of ordinal
878        // USIM only UICC.)
879        // If PROPERTY_TEST_CSIM is defined, bypess provision check
880        // and consider the SIM is provisioned.
881        if (SystemProperties.getBoolean(PROPERTY_TEST_CSIM, false)) {
882            return true;
883        }
884
885        if (mParentApp == null) {
886            return false;
887        }
888
889        if (mParentApp.getType() == AppType.APPTYPE_CSIM &&
890            ((mMdn == null) || (mMin == null))) {
891            return false;
892        }
893        return true;
894    }
895
896    @Override
897    public void setVoiceMessageWaiting(int line, int countWaiting) {
898        // Will be used in future to store voice mail count in UIM
899        // C.S0023-D_v1.0 does not have a file id in UIM for MWI
900        log("RuimRecords:setVoiceMessageWaiting - NOP for CDMA");
901    }
902
903    @Override
904    public int getVoiceMessageCount() {
905        // Will be used in future to retrieve voice mail count for UIM
906        // C.S0023-D_v1.0 does not have a file id in UIM for MWI
907        log("RuimRecords:getVoiceMessageCount - NOP for CDMA");
908        return 0;
909    }
910
911    private void handleRuimRefresh(IccRefreshResponse refreshResponse) {
912        if (refreshResponse == null) {
913            if (DBG) log("handleRuimRefresh received without input");
914            return;
915        }
916
917        if (refreshResponse.aid != null &&
918                !refreshResponse.aid.equals(mParentApp.getAid())) {
919            // This is for different app. Ignore.
920            return;
921        }
922
923        switch (refreshResponse.refreshResult) {
924            case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE:
925                if (DBG) log("handleRuimRefresh with SIM_REFRESH_FILE_UPDATED");
926                mAdnCache.reset();
927                fetchRuimRecords();
928                break;
929            case IccRefreshResponse.REFRESH_RESULT_INIT:
930                if (DBG) log("handleRuimRefresh with SIM_REFRESH_INIT");
931                // need to reload all files (that we care about)
932                onIccRefreshInit();
933                break;
934            case IccRefreshResponse.REFRESH_RESULT_RESET:
935                // Refresh reset is handled by the UiccCard object.
936                if (DBG) log("handleRuimRefresh with SIM_REFRESH_RESET");
937                break;
938            default:
939                // unknown refresh operation
940                if (DBG) log("handleRuimRefresh with unknown operation");
941                break;
942        }
943    }
944
945    public String getMdn() {
946        return mMdn;
947    }
948
949    public String getMin() {
950        return mMin;
951    }
952
953    public String getSid() {
954        return mHomeSystemId;
955    }
956
957    public String getNid() {
958        return mHomeNetworkId;
959    }
960
961    public boolean getCsimSpnDisplayCondition() {
962        return mCsimSpnDisplayCondition;
963    }
964    @Override
965    protected void log(String s) {
966        Rlog.d(LOG_TAG, "[RuimRecords] " + s);
967    }
968
969    @Override
970    protected void loge(String s) {
971        Rlog.e(LOG_TAG, "[RuimRecords] " + s);
972    }
973
974    @Override
975    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
976        pw.println("RuimRecords: " + this);
977        pw.println(" extends:");
978        super.dump(fd, pw, args);
979        pw.println(" mOtaCommited=" + mOtaCommited);
980        pw.println(" mMyMobileNumber=" + mMyMobileNumber);
981        pw.println(" mMin2Min1=" + mMin2Min1);
982        pw.println(" mPrlVersion=" + mPrlVersion);
983        pw.println(" mEFpl[]=" + Arrays.toString(mEFpl));
984        pw.println(" mEFli[]=" + Arrays.toString(mEFli));
985        pw.println(" mCsimSpnDisplayCondition=" + mCsimSpnDisplayCondition);
986        pw.println(" mMdn=" + mMdn);
987        pw.println(" mMin=" + mMin);
988        pw.println(" mHomeSystemId=" + mHomeSystemId);
989        pw.println(" mHomeNetworkId=" + mHomeNetworkId);
990        pw.flush();
991    }
992}
993